<!DOCTYPE html>
<html lang="zh-Hans">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 3.8.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"yoursite.com","root":"/","scheme":"Gemini","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":false,"show_result":false,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta name="description" content="A Quiet Departure.">
<meta property="og:type" content="website">
<meta property="og:title" content="Feng&#39;s Blog">
<meta property="og:url" content="http://yoursite.com/index.html">
<meta property="og:site_name" content="Feng&#39;s Blog">
<meta property="og:description" content="A Quiet Departure.">
<meta property="og:locale" content="zh-Hans">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Feng&#39;s Blog">
<meta name="twitter:description" content="A Quiet Departure.">

<link rel="canonical" href="http://yoursite.com/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : true,
    isPost : false,
    lang   : 'zh-Hans'
  };
</script>

  <title>Feng's Blog</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="Toggle navigation bar">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">Feng's Blog</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="fa fa-home fa-fw"></i>Home</a>

  </li>
        <li class="menu-item menu-item-about">

    <a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>About</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>Tags</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>Categories</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>Archives</a>

  </li>
  </ul>
</nav>




</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content index posts-expand">
            
      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-Hans">
    <link itemprop="mainEntityOfPage" href="http://yoursite.com/2020/09/11/数据结构系列之树-Java/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="Fengorz">
      <meta itemprop="description" content="A Quiet Departure.">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Feng's Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2020/09/11/数据结构系列之树-Java/" class="post-title-link" itemprop="url">数据结构系列之树（Java）</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">Posted on</span>

              <time title="Created: 2020-09-11 21:33:45" itemprop="dateCreated datePublished" datetime="2020-09-11T21:33:45+08:00">2020-09-11</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">Edited on</span>
                <time title="Modified: 2020-09-12 13:48:48" itemprop="dateModified" datetime="2020-09-12T13:48:48+08:00">2020-09-12</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">In</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/数据结构/" itemprop="url" rel="index"><span itemprop="name">数据结构</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h2 id="树的定义"><a href="#树的定义" class="headerlink" title="树的定义"></a>树的定义</h2><p>树是由n个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树，也就是说它是根朝上，而叶朝下的。<br>当n=0时，称为空树。<br><img src="https://gitee.com/fengorz/oss/raw/master/uPic/pbMQ0p.png" alt></p>
<h2 id="树的特点"><a href="#树的特点" class="headerlink" title="树的特点"></a>树的特点</h2><ul>
<li>每个节点都只有有限个子节点或无子节点；</li>
<li>没有父节点的节点称为根节点；</li>
<li>每一个非根节点有且只有一个父节点；</li>
<li>除了根节点外，每个子节点可以分为多个不相交的子树；</li>
<li>树里面没有环路(cycle)。 </li>
</ul>
<h2 id="树的术语"><a href="#树的术语" class="headerlink" title="树的术语"></a>树的术语</h2><ul>
<li>根节点： 根节点没有父节点，树中最多有一个根节点（如上图的Root）；</li>
<li>节点的度：一个节点含有的子树的个数称为该节点的度（如上图的Root的度为3，L2_*所有节点的度为0）；</li>
<li>树的度：树中最大的节点度称为树的度（如上图树的度=Root的度=L1_C的度）；</li>
<li>叶节点（终端节点）：度为0的节点（如上图L2_*的所有节点）；</li>
<li>父节点：若一个节点含有子节点，则这个节点称为其子节点的父节点；</li>
<li>子节点：一个结点的子树的根节点称为其孩子结点；</li>
<li>兄弟节点：具有相同父节点的节点互称为兄弟节点；</li>
<li>树的层（节点的层）：从根开始定义起，根为第1层，根的子节点为第2层，以此类推；</li>
<li>分支节点（非终端节点）：度不为零的节点；</li>
<li>深度：对于任意节点n,n的深度为从根到n的唯一路径长，根的深度为0；</li>
<li>高度：对于任意节点n,n的高度为从n到其子孙中的最长路径，所有树叶的高度为0；</li>
<li>节点的祖先：从根到该节点所经分支上的所有节点；</li>
<li>森林：由m（m&gt;=0）棵互不相交的树的集合称为森林；</li>
<li>子孙：以某节点为根的子树中任一节点都称为该节点的子孙。</li>
</ul>
<h2 id="二叉树"><a href="#二叉树" class="headerlink" title="二叉树"></a>二叉树</h2><h3 id="二叉查找树（排序二叉树、Binary-Seary-Tree、二叉搜索树、有序二叉树）"><a href="#二叉查找树（排序二叉树、Binary-Seary-Tree、二叉搜索树、有序二叉树）" class="headerlink" title="二叉查找树（排序二叉树、Binary Seary Tree、二叉搜索树、有序二叉树）"></a>二叉查找树（排序二叉树、Binary Seary Tree、二叉搜索树、有序二叉树）</h3><h3 id="完全二叉树"><a href="#完全二叉树" class="headerlink" title="完全二叉树"></a>完全二叉树</h3><h4 id="满二叉树"><a href="#满二叉树" class="headerlink" title="满二叉树"></a>满二叉树</h4><h2 id="霍夫曼树"><a href="#霍夫曼树" class="headerlink" title="霍夫曼树"></a>霍夫曼树</h2><h2 id="B树"><a href="#B树" class="headerlink" title="B树"></a>B树</h2>
      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-Hans">
    <link itemprop="mainEntityOfPage" href="http://yoursite.com/2020/08/04/学海无涯苦作舟/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="Fengorz">
      <meta itemprop="description" content="A Quiet Departure.">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Feng's Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2020/08/04/学海无涯苦作舟/" class="post-title-link" itemprop="url">学海无涯苦作舟</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">Posted on</span>

              <time title="Created: 2020-08-04 19:13:39" itemprop="dateCreated datePublished" datetime="2020-08-04T19:13:39+08:00">2020-08-04</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">Edited on</span>
                <time title="Modified: 2020-09-12 13:01:30" itemprop="dateModified" datetime="2020-09-12T13:01:30+08:00">2020-09-12</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">In</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/Learning/" itemprop="url" rel="index"><span itemprop="name">Learning</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h1 id="专业方法和技巧"><a href="#专业方法和技巧" class="headerlink" title="专业方法和技巧"></a>专业方法和技巧</h1><h2 id="第一原理（第一性原理、第一思维方式）"><a href="#第一原理（第一性原理、第一思维方式）" class="headerlink" title="第一原理（第一性原理、第一思维方式）"></a>第一原理（第一性原理、第一思维方式）</h2><p><a href="https://wiki.mbalib.com/wiki/%E7%AC%AC%E4%B8%80%E6%80%A7%E5%8E%9F%E7%90%86" target="_blank" rel="noopener">https://wiki.mbalib.com/wiki/%E7%AC%AC%E4%B8%80%E6%80%A7%E5%8E%9F%E7%90%86</a><br>“我们运用第一性原理，而不是比较思维去思考问题是非常重要的。我们在生活中总是倾向于比较，对别人已经做过或者正在做的事情我们也都去做，这样发展的结果只能产生细小的迭代发展。第一性原理的思想方式是用物理学的角度看待世界，也就是说一层层拨开事物表象，看到里面的本质，再从本质一层层往上走。”这是马斯克眼中的“第一性原理思维模型”——回溯事物的本质，重新思考怎么做。</p>
<h2 id="费曼4步学习法"><a href="#费曼4步学习法" class="headerlink" title="费曼4步学习法"></a>费曼4步学习法</h2><h2 id="艾宾浩斯记忆曲线"><a href="#艾宾浩斯记忆曲线" class="headerlink" title="艾宾浩斯记忆曲线"></a>艾宾浩斯记忆曲线</h2><p>遗忘曲线（Forgetting curve）是用于表述记忆中的中长期记忆的遗忘率的一种曲线。这一曲线最早由心理学家赫尔曼·艾宾浩斯通过自己的实验提出。在这一实验中，艾宾浩斯使用了一些毫无意义的字母组合。通过记忆这些字母组合，并在一系列时间间隔后检查遗忘率，得到了这一曲线。因此，这一曲线又叫艾宾浩斯遗忘曲线。<br>记忆曲线大概如下：</p>
<ul>
<li>20分钟后，42%被遗忘掉，58%被记住。</li>
<li>1小时后，56%被遗忘掉，44%被记住。</li>
<li>1天后，74%被遗忘掉，26%被记住。</li>
<li>1周后，77%被遗忘掉，23%被记住。</li>
<li>1个月后，79%被遗忘掉，21%被记住。</li>
</ul>
<blockquote>
<p>Anki Notes这个软件就是根据这个原理设计的。</p>
</blockquote>
<h2 id="罗斯福专业法"><a href="#罗斯福专业法" class="headerlink" title="罗斯福专业法"></a>罗斯福专业法</h2><h2 id="达芬奇笔记"><a href="#达芬奇笔记" class="headerlink" title="达芬奇笔记"></a>达芬奇笔记</h2><h1 id="一些个专有名词"><a href="#一些个专有名词" class="headerlink" title="一些个专有名词"></a>一些个专有名词</h1><h2 id="边际效益"><a href="#边际效益" class="headerlink" title="边际效益"></a>边际效益</h2><p><a href="https://baike.baidu.com/item/%E8%BE%B9%E9%99%85%E6%95%88%E7%9B%8A/2414098?fr=aladdin" target="_blank" rel="noopener">https://baike.baidu.com/item/%E8%BE%B9%E9%99%85%E6%95%88%E7%9B%8A/2414098?fr=aladdin</a></p>
<h2 id="方法论-VS-世界观"><a href="#方法论-VS-世界观" class="headerlink" title="方法论 VS 世界观"></a>方法论 VS 世界观</h2><p><strong><em>它是人们用什么样的方式、方法来观察事物和处理问题。概括地说，世界观主要说明世界“是什么”的问题，方法论主要说明“怎么办”的问题。</em></strong><br>方法论是一种以解决问题为目标的理论体系或系统，通常涉及对问题阶段、任务、工具、方法技巧的论述。方法论会对一系列具体的方法进行分析研究、系统总结并最终提出较为一般性的原则。<br><a href="https://baike.baidu.com/item/%E6%96%B9%E6%B3%95%E8%AE%BA/82748?fr=aladdin" target="_blank" rel="noopener">方法论的百度百科</a></p>
<h1 id="成语-amp-词语"><a href="#成语-amp-词语" class="headerlink" title="成语&amp;词语"></a>成语&amp;词语</h1><ul>
<li>睚眦必报</li>
<li>兼听则明，偏听则暗</li>
<li>触类旁通</li>
<li>高光时刻</li>
<li>窸窸窣窣</li>
<li>侧足而立</li>
<li>相悖</li>
<li>商榷</li>
<li>结痂</li>
<li>一骑绝尘</li>
</ul>
<h1 id="权威书籍"><a href="#权威书籍" class="headerlink" title="权威书籍"></a>权威书籍</h1><h2 id="《金字塔原理》"><a href="#《金字塔原理》" class="headerlink" title="《金字塔原理》"></a>《金字塔原理》</h2><h2 id="《罗伯特议事规则》"><a href="#《罗伯特议事规则》" class="headerlink" title="《罗伯特议事规则》"></a>《罗伯特议事规则》</h2>
      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-Hans">
    <link itemprop="mainEntityOfPage" href="http://yoursite.com/2020/07/09/《深入理解Java虚拟机第3版》阅读笔记/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="Fengorz">
      <meta itemprop="description" content="A Quiet Departure.">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Feng's Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2020/07/09/《深入理解Java虚拟机第3版》阅读笔记/" class="post-title-link" itemprop="url">《深入理解Java虚拟机第3版》阅读笔记</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">Posted on</span>

              <time title="Created: 2020-07-09 09:55:12" itemprop="dateCreated datePublished" datetime="2020-07-09T09:55:12+08:00">2020-07-09</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">Edited on</span>
                <time title="Modified: 2020-09-11 21:21:07" itemprop="dateModified" datetime="2020-09-11T21:21:07+08:00">2020-09-11</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">In</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/Java/" itemprop="url" rel="index"><span itemprop="name">Java</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <blockquote>
<p>这篇笔记总结自周志明老师的《深入理解Java虚拟机第3版》，以及其他网上知识点收集，只是我自己的个人复习笔记，禁止转载，如有侵权，请联系我删除。<br>思维导图版本<a href="http://www.xmind.net/m/MfZ5zm" target="_blank" rel="noopener">点击这里</a></p>
</blockquote>
<h1 id="虚拟机概述"><a href="#虚拟机概述" class="headerlink" title="虚拟机概述"></a>虚拟机概述</h1><h2 id="中间语言翻译（从Java翻译到机器码）"><a href="#中间语言翻译（从Java翻译到机器码）" class="headerlink" title="中间语言翻译（从Java翻译到机器码）"></a>中间语言翻译（从Java翻译到机器码）</h2><blockquote>
<p>从中间语言翻译到机器码</p>
</blockquote>
<h3 id="通过C语言翻译（初JVM）"><a href="#通过C语言翻译（初JVM）" class="headerlink" title="通过C语言翻译（初JVM）"></a>通过C语言翻译（初JVM）</h3><p>通过将字节码class翻译成C语言，比如定义iadd这个字节码是相加的助记符，然后通过c语言来解析字节码中的iadd就是相加，并指向对应的c语言相加的c程序来做实际运算，这样子的话，这个解析字节码的C程序就是所谓的JVM，最简陋效率最低的初代JVM</p>
<h3 id="直接翻译为机器码"><a href="#直接翻译为机器码" class="headerlink" title="直接翻译为机器码"></a>直接翻译为机器码</h3><h4 id="CS-IP"><a href="#CS-IP" class="headerlink" title="CS:IP"></a>CS:IP</h4><p>CS与IPO这是物理CPU内部的两个寄存器。对于一台物理机器而言，这两个寄存器是最重要的寄存器，因为CPU在取指令时便完全依靠这两个寄存器。CS寄存器保存段地址，IP保存偏移地址。CS和IP这两个寄存器的值能够唯一确定内存中的一个地址，CPU在执行机器指令之前，便通过这两个寄存器定位到目标内存位置，并将该位置处的机器指令取出来进行运算。函数跳转的本质其实便是修改CS和IP这两个寄存器的内容，使其指向到目标函数所在内存的首地址，这样CPU便能执行目标函数了。Java虚拟机要想让物理CPU直接执行Java程序所对应的目标机器码，也得修改这两个寄存器才能实现。</p>
<p>在C语言中，能够使用语法糖来修改CS:IP段寄存器，这样C程序就能直接动态执行机器码。<br>既然都能在C语言中直接动态执行机器码了（可以简单理解为：C语言的变量里面可以复制解析出来的机器码，然后直接执行），我们只要将中间语言指令直接翻译为机器码，然后让CS:IP直接指向这段机器码执行，这也是现代JVM最重要的的基本原理之一。</p>
<h3 id="本地编译"><a href="#本地编译" class="headerlink" title="本地编译"></a>本地编译</h3><p>中间语言（Java）与同样属于高级语言的C语言相比，它们实现相同的功能，C语言编译后所生成的机器码，也比中间语言直接翻译成的机器码，在数量上要精简很多，所以单从这个角度来讲，C语言的执行效率当然是要更高一些。<br>中间语言由于其本身不能直接被CPU执行，为了能够被CPU执行，中间语言在完成同样一个功能时，需要准备更多便于自我管理的上下文环境，最后才能执行目标机器指令。准备上下文环境最终也是依靠机器码去实现，因此中间语言最终便生成了更多机器码，当然执行效率就降低了。<br>为了提高性能，JVM提供了一种机制能够将中间语言(字节码)直接编译为本地机器指令。例如安卓和部分JVM所实现的AOT ( ahead of time )特性便是这方面的尝试，但是这种方式并没有减少机器指令的数量级问题。<br>除此之外，JVM的大牛们在JIT (即时编译)、内存分配也进行了大量的优化，使JVM能够对热点代码进行大幅度指令优化，正是由于JVM可以在运行期基于上下文链路进行各种优化，因此优化后的指令质量甚至比C/C++编译岀的指令质量更高，以及部分Java程序性能甚至反超C/C++程序。如果离开了这些动态优化，Java程序的执行效率是无论如何也提不上去的。</p>
<h2 id="神奇的指令"><a href="#神奇的指令" class="headerlink" title="神奇的指令"></a>神奇的指令</h2><p>Java虚拟机其实是”读不懂”Java代码的，不过也并非所有的虚拟机都不懂得面向对象的语言，JavaScript执行引擎就是个例外一JS脚本不需要编译就能被JS引擎直接执行。<br>Java所谓的中间语言就是Java字节码指令集，指令集一般是计算机硬件才有的东西，而作者却在软件层面定义了一套同样的东西。但是，软件本身不具备执行程序的能力，软件最终还得依靠硬件指令才能完成逻辑计算。因此，一套好的软件指令必须不能超出硬件指令所能表达的计算能力，同时又要对硬件指令进行高度抽象与概括。换言之，如果你定义了一套与硬件指令集完全一模一样的软件指令集，那大家还用你干嘛呀，不如直接用硬件指令得了。</p>
<hr>
<h1 id="Java技术的未来"><a href="#Java技术的未来" class="headerlink" title="Java技术的未来"></a>Java技术的未来</h1><h2 id="无语言倾向"><a href="#无语言倾向" class="headerlink" title="无语言倾向"></a>无语言倾向</h2><h3 id="GraalJVM"><a href="#GraalJVM" class="headerlink" title="GraalJVM"></a>GraalJVM</h3><p><img src="https://gitee.com/fengorz/oss/raw/master/uPic/0eyudF.png" alt></p>
<h3 id="新一代即时编译器"><a href="#新一代即时编译器" class="headerlink" title="新一代即时编译器"></a>新一代即时编译器</h3><p>HotSpot虚拟机中含有两个即使编译器：</p>
<ul>
<li>编译耗时短但是输出代码优化程度较低的客户端编译器（C1）。</li>
<li>编译耗时长但输出代码优化质量较高的服务端编译器（C2）。</li>
</ul>
<p>JDK10之后，HotSpot加入了一个全新的即时编译器，Graal编译器(可以通过相应的JVM参数配置启动，以C2代替者的身份登场，目前还是处于”实验状态”)。<br>Graal能够做比C2更加复杂的优化，如“部分逃逸分析”(Partial Escape Analysis)，也拥有比C2更容易使用激进预测性优化(Aggressive Speculative Optimization)的策略，支持自定义的预测性假设等。</p>
<h3 id="向Native迈进"><a href="#向Native迈进" class="headerlink" title="向Native迈进"></a>向Native迈进</h3><p>最新的基本版本的JDK已经推出跨进程的、可以面向用户程序的类型信息共享(Application Class Data Sharing, AppCDS,允许把加载解析后的类型信息缓存起来，从而提升下次启动速度。</p>
<h4 id="Substrate-VM"><a href="#Substrate-VM" class="headerlink" title="Substrate VM"></a>Substrate VM</h4><p>Substrate VM在内存占用和启动时间比HotSpot强5到50倍：<br><img src="https://cdn.jsdelivr.net/gh/coding-by-feng/oss@master/uPic/eoIO68.png" alt><br><img src="https://cdn.jsdelivr.net/gh/coding-by-feng/oss@master/uPic/yutz7e.png" alt></p>
<h2 id="实战：自己编译JDK"><a href="#实战：自己编译JDK" class="headerlink" title="实战：自己编译JDK"></a>实战：自己编译JDK</h2><h3 id="OpenJDK-vs-OracleJDK"><a href="#OpenJDK-vs-OracleJDK" class="headerlink" title="OpenJDK vs OracleJDK"></a>OpenJDK vs OracleJDK</h3><p>实际上，JDK11之后，两者的公用源码已经占比很高了：<br><img src="https://cdn.jsdelivr.net/gh/coding-by-feng/oss@master/uPic/BzIIY0.png" alt></p>
<h1 id="自动内存管理"><a href="#自动内存管理" class="headerlink" title="自动内存管理"></a>自动内存管理</h1><h2 id="运行时数据区域"><a href="#运行时数据区域" class="headerlink" title="运行时数据区域"></a>运行时数据区域</h2><p><img src="https://cdn.jsdelivr.net/gh/coding-by-feng/oss@master/uPic/LxyOJy.png" alt></p>
<h3 id="程序计数器"><a href="#程序计数器" class="headerlink" title="程序计数器"></a>程序计数器</h3><p>程序计数器(Program Counter Register)是一块较小的内存空间，它可以看作是当前线程所执行的字节码的行号指示器。在Java虚拟机的<code>概念模型</code>里，字节解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令，它是程序控制流的指示器，分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。<br>每个线程都有一个独立的程序计数器，各线程之间的计数器互不影响（因为Java多线程是采用线程轮流切换来实现的，多核处理器中的单个处理器一次只能执行某个线程中的某条指令，切换之后各自的计数器能够恢复到各自接下来的执行位置）。<br>所以，<strong><em>程序计数器是线程私有的。</em></strong></p>
<h3 id="Java虚拟机栈"><a href="#Java虚拟机栈" class="headerlink" title="Java虚拟机栈"></a>Java虚拟机栈</h3><p><strong><em>Java虚拟机栈一样是线程私有的，生命周期与线程相同。</em></strong><br>虚拟机栈描述的是Java方法执行的线程内存模型，每个方法被执行的时候，Java虚拟机都会同步创建一一个<code>栈帧(Stack Frame)</code>用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程，就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。<br>广为流传的所谓”堆”和”栈”内存划分说话中的”栈”就是指虚拟机栈，或者是指虚拟机栈中的局部变量表部分。</p>
<h4 id="栈帧？"><a href="#栈帧？" class="headerlink" title="栈帧？"></a>栈帧？</h4><h4 id="局部变量表"><a href="#局部变量表" class="headerlink" title="局部变量表"></a>局部变量表</h4><p>局部变量表存放了编译期可知的各种Java虚拟机基本数据类型(boolean、byte、char、 short、 int、 float、 long、 double) 、对象引用(reference类型，它并不等同于对象本身，可能是一个指向对象起始地址的<code>引用指针</code>，或者叫<code>直接指针</code>；也可能是指向一个代表对象的<code>句柄</code>或者其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)<br>这些数据类型在局部变量表中的存储空间以局部变量槽(Slot)来表示，其中64位长度的1ong和double类型的数据会占用两个变量槽，其余的数据类型只占用一个。局部变量表所需的内存空间在编译期间完成分配，当进入-一个方法时，这个方法需要在栈帧中分配多大的局部变量空间是完全确定的，在方法运行期间不会改变局部变量表的大小。（“大小”是指变量槽的数量，变量槽占用32个比特还是64个比特由虚拟机决定）。</p>
<h4 id="StackOverFlowError和OutOfMemoryError（Java虚拟机规范）"><a href="#StackOverFlowError和OutOfMemoryError（Java虚拟机规范）" class="headerlink" title="StackOverFlowError和OutOfMemoryError（Java虚拟机规范）"></a>StackOverFlowError和OutOfMemoryError（Java虚拟机规范）</h4><ul>
<li>如果线程请求的栈深度大于虚拟机所允许的深度，将拋出StackOverflowError异常; </li>
<li>如果Java虚拟机栈容量可以动态扩展，当栈扩展时无法申请到足够的内存会拋出OutOfMemoryError异常。</li>
</ul>
<h4 id="操作数栈？"><a href="#操作数栈？" class="headerlink" title="操作数栈？"></a>操作数栈？</h4><h3 id="本地方法栈"><a href="#本地方法栈" class="headerlink" title="本地方法栈"></a>本地方法栈</h3><p>本地方法栈（Native Method Stacks）为虚拟机调用本地方法而服务，就像虚拟机栈服务于Java方法的调用一样。<br>虚拟机可以对本地方法栈自由实现，《Java虚拟机规范》对这没有强制约束（HotSpot将虚拟机栈和本地方法栈合并了）。</p>
<h3 id="Java堆（Heap、GC堆）"><a href="#Java堆（Heap、GC堆）" class="headerlink" title="Java堆（Heap、GC堆）"></a>Java堆（Heap、GC堆）</h3><p>Java堆是虚拟机内存管理中最大的一块（线程共享）。<br>当前主流虚拟机都可以通过参数扩展堆的大小（<code>-Xmx</code>、<code>-Xms</code>），如果内存不足或者堆无法再扩展了，就会抛出OutOfMemoryError。</p>
<h3 id="方法区（”非堆”、Non-Heap）"><a href="#方法区（”非堆”、Non-Heap）" class="headerlink" title="方法区（”非堆”、Non-Heap）"></a>方法区（”非堆”、Non-Heap）</h3><p>和Java堆一样，是各个线程共享的内存区域，它用于存储已被虚拟机加载的<code>类型信息</code>、<code>常量</code>、<code>静态变量</code>、<code>即时编译器编译后的代码缓存</code>等数据。<br>虽然《Java虚拟机规范》中把方法区描述为堆的一个逻辑部分，但是它却有一个别名叫作“非堆”(Non-Heap) ，目的是与Java堆区分开来。</p>
<h4 id="永久代（Permanent-Generation）的落寞，元空间（Meta-space）的登场"><a href="#永久代（Permanent-Generation）的落寞，元空间（Meta-space）的登场" class="headerlink" title="永久代（Permanent Generation）的落寞，元空间（Meta-space）的登场"></a>永久代（Permanent Generation）的落寞，元空间（Meta-space）的登场</h4><p>Java 8之前，很多人习惯将方法区叫称为”永久代”，因为仅仅只有当时的HotSpot使用永久代来实现方法区，很多其他的虚拟机并不存在永久代的概念，《Java虚拟机规范》也对方法区的实现没有约束，所以方法区不等同于永久代。</p>
<blockquote>
<p>HotSpot使用永久代来实现方法区，实际上导致了Java应用更容易出现内存溢出，其<code>-XX: MaxPermSize</code>可以设置永久代的上限，即使不设置也有默认大小。 &gt; 而J9和JRockit就没有此类内存限制，除非触碰进程可用内存上限，这个是由系统控制。</p>
</blockquote>
<p>所以到了JDK 6时，HotSpot就逐步放弃永久代，开始采用<code>本地内存（Native Memory）</code>来实现方法区了，到了JDK 8就完全放弃永久代了，改用在本地内存中实现的<code>元空间（Meta-space）</code>，把字符串常量池、静态变量、类型信息等都移到元空间了。<br>垃圾收集器在方法区的回收行为出现得是比较少的，这个区域的内存回收只要是<strong><em>针对常量池的回收和对类型的卸载</em></strong>。</p>
<h4 id="运行时常量池（Runtime-Constant-Pool）"><a href="#运行时常量池（Runtime-Constant-Pool）" class="headerlink" title="运行时常量池（Runtime Constant Pool）"></a>运行时常量池（Runtime Constant Pool）</h4><p>运行时常量池是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外，还有一项信息是常量池表(Constant Pool Table)，用于存放编译期生成的各种字面量与符号引用，这部分内容将在类加载后存放到方法区的运行时常量池中。<br>Java虚拟机对于Class文件每一部分(自然也包括常量池)的格式都有严格规定，如每一个字节用于存储哪种数据都必须符合规范上的要求才会被虚拟机认可、加载和执行，但对于运行时常量池，《Java虚拟机规范》并没有做任何细节的要求。<br>运行时常量池相较于<code>Class文件常量池</code>具有动态性，运行期间可以将新的常量放入运行时常量池中，比如<code>String.intern</code>。<br>因为运行时常量池属于方法区，自然也就受到OutOfMemoryError异常的约束。</p>
<h4 id="直接内存（Direct-Memory）"><a href="#直接内存（Direct-Memory）" class="headerlink" title="直接内存（Direct Memory）"></a>直接内存（Direct Memory）</h4><p>直接内存并不是虚拟机运行时数据区的一部分，也不是《Java虚拟机规范》中定义的内存区域。但是这部分内存也被频繁地使用，而且也可能导致OutOfMemoryError异常出现。<br>在JDK 1.4中新加入了NIO (New Input/Output)类，引入了一种基于通道(Channel)与缓冲区(Buffer) 的I/O方式，它可以使用Native函数库直接分配堆外内存，然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能，因为避免了在Java堆和Native堆中来回复制数据，直接内存当然也是受到物理总内存的约束。</p>
<blockquote>
<p>所以在设置<code>-Xmx</code>等参数时，除了考虑运行时数据区里的各大内存区域，还要考虑到直接内存。 </p>
</blockquote>
<h2 id="垃圾收集器与内存分配策略"><a href="#垃圾收集器与内存分配策略" class="headerlink" title="垃圾收集器与内存分配策略"></a>垃圾收集器与内存分配策略</h2><h3 id="对象的创建"><a href="#对象的创建" class="headerlink" title="对象的创建"></a>对象的创建</h3><p>对象内存分配的方式（选择哪种分配方式由Java堆是否规整决定，而Java堆是否规整又由所采用的垃圾收集器是否带有空间压缩整理(Compact) 的能力决定）：</p>
<ul>
<li>指针碰撞（Bump The Pointer）：当使用Serial、ParNew等带压缩整理过程的收集器时（简单高效）。</li>
<li>空闲列表（Free List）：当使用CMS这种基于清除(Sweep)算法的收集器时（较为复杂，实际上，在空闲列表中如果还能够拿到一大块子内存的话，CMS的实现还采用了一种可以继续使用指针碰撞的方式，叫做Linear Allocation Buffer）。</li>
</ul>
<h4 id="并发情况下的内存分配线程安全问题"><a href="#并发情况下的内存分配线程安全问题" class="headerlink" title="并发情况下的内存分配线程安全问题"></a>并发情况下的内存分配线程安全问题</h4><p>并发条件下，可能出现正在给对象A分配内存，指针还没来得及修改，对象B又同时使用了原来的指针来分配内存的情况，解决这个问题的方案：</p>
<ul>
<li>采用CAS配上失败重试的方式保证更新操作的原子性；</li>
<li>把内存分配的动作按照线程划分，在不同的空间之中进行，即每个线程在Java堆中预先分配一小块内存，称为本地线程分配缓冲(Thread Local Allocation Buffer, TLAB)，线程在各自的本地缓冲区中分配内存，只有本地缓冲区用完了，分配新的缓存区时才需要同步锁定。</li>
</ul>
<p>可以通过<code>-XX: +/-UseTLAB</code>参数来设定使用哪种方案。</p>
<h3 id="对象的内存布局"><a href="#对象的内存布局" class="headerlink" title="对象的内存布局"></a>对象的内存布局</h3><p>对象在堆内存中的存储布局：</p>
<h4 id="对象头（Header）"><a href="#对象头（Header）" class="headerlink" title="对象头（Header）"></a>对象头（Header）</h4><p>HotSpot对象头包含两类信息：</p>
<ul>
<li>存储对象自身的运行时数据：<code>哈希码（HashCode）</code>、<code>GC分代年龄</code>、<code>锁状态标志</code>、<code>线程持有的锁</code>、<code>偏向线程ID</code>、<code>偏向时间戳</code>等，官方称之为”Mark Word”。<br>MarkWord被设计成一一个有着动态定义的数据结构，以便在极小的空间内存储尽量多的数据，根据对象的状态复用自己的存储空间。例如在32位的HotSpot虚拟机中，如对象未被同步锁锁定的状态下，Mark Word的32个比特存储空间中的25个比特用于存储对象哈希码，4个比特用于存储对象分代年龄，2个比特用于存储锁标志位，1个比特固定为0，在其他状态(轻量级锁定、重量级锁定、GC标记、可偏向) 下对象的存储内容。如下图：<br><img src="https://gitee.com/fengorz/oss/raw/master/uPic/YAJQr9.png" alt></li>
<li>类型指针：即对象指向 它的类型元数据的指针，Java虚拟机通过这个指针来确定该对象是哪个类的实例。并不是所有的虚拟机实现都必须在对象数据上保留类型指针。</li>
</ul>
<h4 id="实例数据（Instance-Data）"><a href="#实例数据（Instance-Data）" class="headerlink" title="实例数据（Instance Data）"></a>实例数据（Instance Data）</h4><p>这部分是对象真正存储的有效信息，即程序代码里定义的各种类型的字段内容，无论是从父类继承下来的，还是在子类中定义的字段都必须记录在此。<br>这部分的存储顺序会受到虚拟机分配策略参数<code>-XX: FieldsAllocationStyle</code>参数和字段在Java源码中定义顺序的影响。<br>HotSpot虛拟机默认的分配顺序为longs/doubles、ints、 shorts/chars、 bytes/booleans、oops(Ordinary Object Pointers, OOPs)，从以上默认的分配策略中可以看到，相同宽度的字段总是被分配到一起存放，在满足这个前提条件的情况下，在父类中定义的变量会出现在子类之前。<br>如果HotSpot虛拟机的<code>+XX: CompactPields</code>参数值为true(默认就为true)，那子类之中较窄的变量也允许插入父类变量的空隙之中，以节省出一点点空间。</p>
<h4 id="对齐填充（Padding）"><a href="#对齐填充（Padding）" class="headerlink" title="对齐填充（Padding）"></a>对齐填充（Padding）</h4><p>不是一定存在的，只是起到占位符的作用。<br>由于HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍，换句话说就是任何对象的大小都必须是8字节的整数倍。对象头部分已经被精心设计成正好是8字节的倍数(1倍或者2倍)，因此，如果对象实例数据部分没有对齐的话，就需要通过对齐填充来补全。</p>
<h3 id="对象的访问定位"><a href="#对象的访问定位" class="headerlink" title="对象的访问定位"></a>对象的访问定位</h3><p>Java程序会通过栈上的reference类型数据来操作堆上的具体对象。</p>
<h4 id="句柄和直接指针两种方式"><a href="#句柄和直接指针两种方式" class="headerlink" title="句柄和直接指针两种方式"></a>句柄和直接指针两种方式</h4><ul>
<li>句柄方式：Java堆中将可能会划分出一块内存来作为句柄池，reference中存储的就是对象的句柄地址，而句柄中包含了对象实例数据与类型数据各自具体的地址信息。<br><img src="https://gitee.com/fengorz/oss/raw/master/uPic/A1pFdi.png" alt></li>
<li>直接指针：Java堆中对象的内存布局就必须考虑如何放置访问类型数据的相关信息，reference中存储的直接就是对象地址，如果只是访问对象本身的话，就不需要多一次间接访问的开销。<br><img src="https://gitee.com/fengorz/oss/raw/master/uPic/ycOjxS.png" alt></li>
</ul>
<blockquote>
<p>使用句柄来访问的最大好处就是reference中存储的是稳定句柄地址，在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针，而reference本身不需要被修改。<br>使用直接指针来访问最大的好处就是速度更快，它节省了-次指针定位的时间开销，由于对象访问在Java中非常频繁，因此这类开销积少成多也是一项极为可观的执行成本。</p>
</blockquote>
<h3 id="实战：OutOfMemoryError异常"><a href="#实战：OutOfMemoryError异常" class="headerlink" title="实战：OutOfMemoryError异常"></a>实战：OutOfMemoryError异常</h3><p>在《Java虚拟机规范》的规定里，除了程序计数器外，虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError (OOM)异常的可能。</p>
<h4 id="Java堆溢出"><a href="#Java堆溢出" class="headerlink" title="Java堆溢出"></a>Java堆溢出</h4><p>要先确认是内存泄露（Memory Leak）还是内存溢出（Memory Overflow）。</p>
<h5 id="Java内存（映像）分析工具（hprof文件分析工具）"><a href="#Java内存（映像）分析工具（hprof文件分析工具）" class="headerlink" title="Java内存（映像）分析工具（hprof文件分析工具）"></a>Java内存（映像）分析工具（hprof文件分析工具）</h5><p>JVM堆转储快照文件（hprof文件）查看分析时，可以指定heapDumpPath：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/export/Domains/rcsv-fm.wd.local/server1/logs</span><br></pre></td></tr></table></figure></p>
<blockquote>
<p>kill -3 or -9 都不会打heap dump，kill -9 java_pid来不及做任何事情就被干掉了，-3会打印thread dump 但是不是heap dump。</p>
</blockquote>
<h5 id="GC-Roots"><a href="#GC-Roots" class="headerlink" title="GC Roots"></a>GC Roots</h5><h4 id="虚拟机栈和本地方法栈溢出"><a href="#虚拟机栈和本地方法栈溢出" class="headerlink" title="虚拟机栈和本地方法栈溢出"></a>虚拟机栈和本地方法栈溢出</h4><p>HotSpot不区分虚拟机栈和本地方法栈，栈容量由<code>-Xss</code>参数设定。</p>
<ul>
<li>1)如果线程请求的栈深度大于虚拟机所允许的最大深度，将抛出StackOverflowError异常。</li>
<li>2)如果虚拟机的栈内存允许动态扩展，当扩展栈容量无法申请到足够的内存时，将拋出OutOfMemoryError异常 。</li>
</ul>
<p>模拟栈溢出：</p>
<ul>
<li>使用-Xss参数减少栈内存容量。</li>
<li>定义了大量的本地变量，增大此<code>方法帧</code>中<code>本地变量表</code>的长度。</li>
<li>通过不断建立线程的方式，在HotSpot上也是可以产生内存溢出异常的（但是这样产生的内存溢出异常和栈空间是否足够并不存在任何直接的关系，主要取决于操作系统本身的内存使用状态，在这种情况下，给每个线程的栈分配的内存越大，反而越容易产生内存溢出异常）。<br>HotSpot虚拟机提供了参数可以控制Java堆和方法区这两部分的内存的最大值，系统的内存分配需要减去最大堆容量，再减去最大方法区容量，由于程序计数器消耗内存很小，可以忽略掉，如果把直接内存和虚拟机进程本身耗费的内存也去掉的话，剩下的内存就由虚拟机栈和本地方法栈来分配了。因此为每个线程分配到的栈内存越大，可以建立的线程数量自然就越少，建立线程时就越容易把剩下的内存耗尽。</li>
</ul>
<blockquote>
<p>无论是由于栈帧太大还是虚拟机栈容量太小，当新的栈帧内存无法分配的时候，HotSpot 虚拟机抛出的都是StackOverflowError异常。</p>
</blockquote>
<h4 id="方法区和运行时常量池溢出"><a href="#方法区和运行时常量池溢出" class="headerlink" title="方法区和运行时常量池溢出"></a>方法区和运行时常量池溢出</h4><p>在JDK 8以后，永久代便完全退出了历史舞台，元空间作为其替代者登场。在默认设置下，方法区的溢出的溢出已经相对不会那么容易出现了。<br>HotSpot提供了一些参数用于防御元空间溢出问题：<br>-XX: MaxMetaspaceSize: 设置元空间最大值，默认是-1，即不限制，或者说只受限于本地内存大小。<br>-XX: MetaspaceSize: 指定元空间的初始空间大小，以字节为单位，达到该值就会触发垃圾收集进行类型卸载，同时收集器会对该值进行调整:如果释放了大量的空间，就适当降低该值;如果释放了很少的空间，那么在不超过-XX:<br>MaxMetaspaceSize (如果设置了的话)的情况下，适当提高该值。<br>-XX: MinMetaspaceFreeRatio: 作用是在垃圾收集之后控制最小的元空间剩余容量的百分比，可减少因为元空间不足导致的垃圾收集的频率。类似的还有-XX: Max-MetaspaceFreeRatio, 用于控制最大的元空间剩余容量的百分比。</p>
<h4 id="本机直接内存溢出"><a href="#本机直接内存溢出" class="headerlink" title="本机直接内存溢出"></a>本机直接内存溢出</h4><p>直接内存(Direct Memory)的容量大小可通过<code>-XX : MaxDirectMemorySize</code>参数来指定，如果不去指定，则默认与Java堆最大值(由-Xmx指定) 一致。<br>越过了DirectByteBuffer类直接通过反射获取Unsafe实例进行内存分配(Unsafe类的getUnsafe()方法指定只有引导类加载器才会返回实例，体现了设计者希望只有虚拟机标准类库里面的类才能使用Unsafe的功能，在JDK 10时才将Unsafe的部分功能通过VarHandle开放给外部使用)，因为虽然使用DirectByteBuffer分配内存也会拋出内存溢出异常，但它抛出异常时并没有真正向操作系统申请分配内存，而是通过计算得知内存无法分配就会在代码里手动抛出溢出异常，真正申请分配内存的方法是Unsafe:allocateMemory()。</p>
<blockquote>
<p>由直接内存导致的内存溢出，一个明显的特征是在Heap Dump文件中不会看见有什么明显的异常情况，如果读者发现内存溢出之后产生的Dump文件很小，而程序中又直接或间接使用了DirectMemory(典型的间接使用就是NIO)，那就可以考虑重点检查一下直接内存方面的原因了。</p>
</blockquote>
<h1 id="垃圾收集器与内存分配策略-1"><a href="#垃圾收集器与内存分配策略-1" class="headerlink" title="垃圾收集器与内存分配策略"></a>垃圾收集器与内存分配策略</h1><ul>
<li>程序计数器、虚拟机栈、本地方法栈的垃圾回收：<br>这3个区域随线程而生，随线程而灭，栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作。每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的(尽管在运行期会由即时编译器进行一些优化，但在基于概念模型的讨论里，大体上可以认为是编译期可知的)，因此这几个区域的内存分配和回收都具备确定性，在这几个区域内就不需要过多考虑如何回收的问题，当方法结束或者线程结束时，内存自然就跟随着回收了。</li>
<li>Java堆和方法区的垃圾回收：<br>两个区域则有着很显著的不确定性，一个接口的多个实现类需要的内存可能会不一样，一个方法所执行的不同条件分支所需要的内存也可能不一样，只有处于运行期间，我们才能知道程序究竟会创建哪些对象，创建多少个对象，这部分内存的分配和回收是动态的。垃圾收集器所关注的正是这部分内存该如何管理。 </li>
</ul>
<h2 id="对象已死？"><a href="#对象已死？" class="headerlink" title="对象已死？"></a>对象已死？</h2><blockquote>
<p>垃圾收集器在对Java堆进行回收前，第一件事情就是要确定这些对象之中哪些还“存活”着，哪些已经“死去”(“死去”即不可能再被任何途径使用的对象)了。</p>
</blockquote>
<h3 id="引用计数算法"><a href="#引用计数算法" class="headerlink" title="引用计数算法"></a>引用计数算法</h3><p>在对象中添加一一个引用计数器，每当有一个地方引用它时，计数器值就加一；当引用失效时，计数器值就减一，任何时刻计数器为零的对象就是不可能再被使用的。<br>但是，在Java领域，至少主流的Java虚拟机里面都没有选用引用计数算法来管理内存，主要原因是，这个看似简单的算法有很多例外情况要考虑，必须要配合大量额外处理才能保证正确地工作，譬如单纯的引用计数就很难解决对象之间相互循环引用的问题。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> * 单纯的引用计数就很难解决对象之间相互循环引用的问题。</span><br><span class="line"> * @Author zhanshifeng</span><br><span class="line"> * @Date 2020/9/8 4:53 PM</span><br><span class="line"> */</span><br><span class="line">public class ReferenceCountingGC &#123;</span><br><span class="line"></span><br><span class="line">    public Object instance;</span><br><span class="line">    private static final int _1MB = 1024 * 1024;</span><br><span class="line">    private byte[] bigSize = new byte[2 * _1MB];</span><br><span class="line"></span><br><span class="line">    public static void testGC() &#123;</span><br><span class="line">        ReferenceCountingGC a = new ReferenceCountingGC();</span><br><span class="line">        ReferenceCountingGC b = new ReferenceCountingGC();</span><br><span class="line">        a.instance = b;</span><br><span class="line">        b.instance = a;</span><br><span class="line">        a = null;</span><br><span class="line">        b = null;</span><br><span class="line"></span><br><span class="line">        // 如果JVM单纯引用计数的话，a和b是不能被回收的，但是实际上是回收了。</span><br><span class="line">        System.gc();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public static void main(String[] args) &#123;</span><br><span class="line">        testGC();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="可达性分析算法（Reachability-Analysis）"><a href="#可达性分析算法（Reachability-Analysis）" class="headerlink" title="可达性分析算法（Reachability Analysis）"></a>可达性分析算法（Reachability Analysis）</h3><h4 id="GC-Roots-1"><a href="#GC-Roots-1" class="headerlink" title="GC Roots"></a>GC Roots</h4><p>这个算法的基本思路就是通过一系列称为<code>GCRoots</code>的根对象作为起始节点集，从这些节点开始，根据引用关系向下搜索，搜索过程所走过的路径称为“引用链”(Reference Chain)，如果某个对象到GC Roots间没有任何引用链相连，或者用图论的话来说就是从GCRoots到这个对象不可达时，则证明此对象是不可能再被使用的。<br><img src="https://gitee.com/fengorz/oss/raw/master/uPic/ecAOJD.png" alt><br>固定可作为GC Roots的对象包括以下几种:</p>
<ul>
<li>在虚拟机栈(栈帧中的本地变量表)中引用的对象，譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。</li>
<li>在方法区中类静态属性引用的对象，譬如Java类的引用类型静态变量。</li>
<li>在方法区中常量引用的对象，譬如字符串常量池(String Table)里的引用。</li>
<li>在本地方法栈中JNI (即通常所说的Native方法)引用的对象。</li>
<li>Java虚拟机内部的引用，如基本数据类型对应的Class对象，一些常驻的异常对象(比如NullPointExcepiton、 OutOfMemoryError)等，还有系统类加载器。</li>
<li>所有被同步锁(synchronized关键字) 持有的对象。</li>
<li>反映Java虚拟机内部情况的JMXBean、 JVMTI中注册的回调、本地代码缓存等。</li>
</ul>
<blockquote>
<p>目前最新的几款垃圾收集器无一例外都具备了局部回收的特征，为了避免GCRoots包含过多对象而过度膨胀，它们在实现上也做出了各种优化处理。</p>
</blockquote>
<h3 id="对象的引用？"><a href="#对象的引用？" class="headerlink" title="对象的引用？"></a>对象的引用？</h3><ul>
<li>强引用（Strongly Reference）是最传统的“引用”的定义，是指在程序代码之中普遍存在的引用赋值，即类似“Object obj=new Object() ‘这种引用关系。无论任何情况下，只要强引用关系还存在，垃圾收集器就永远不会回收掉被引用的对象。</li>
<li>软引用（Soft Reference）是用来描述一些还有用，但非必须的对象。只被软引用关联着的对象，在系统将要发生内存溢出异常前，会把这些对象列进回收范围之中进行第二次回收，如果这次回收还没有足够的内存，才会抛出内存溢出异常。在JDK 1.2版之后提供了SoftReference类来实现软引用。</li>
<li>弱引用（Weak Reference）也是用来描述那些非必须对象，但是它的强度比软引用更弱一些，被弱引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作，无论当前内存是否足够，都会回收掉只被弱引用关联的对象。在JDK 1.2版之后提供了WeakReference类来实现弱引用。</li>
<li>虚引用也称为“幽灵引用”或者“幻影引用”（Phantom Reference），它是最弱的一种引用关系。一个对象是否有虚引用的存在，完全不会对其生存时间构成影响，也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。在JDK1.2版之后提供了PhantomReference类来实现虚引用。</li>
</ul>
<h3 id="生存与死亡？"><a href="#生存与死亡？" class="headerlink" title="生存与死亡？"></a>生存与死亡？</h3><p>即使在可达性分析算法中判定为不可达的对象，也不是“非死不可”的，这时候它们暂时还处于“缓刑”阶段，要真正宣告一个对象死亡，至少要经历两次标记过程：如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链，那它将会被第一次标记， 随后进行一次筛选，筛选的条件是此对象是否有必要执行finalize(方法。假如对象没有覆盖finalize()方法，或者finalize(方法已经被虚拟机调用过，那么虚拟机将这两种情况都视为“没有必要执行”。如果这个对象被判定为确有必要执行finalize方法，那么该对象将会被放置在一个名为F-Queue的队列之中，并在稍后由一条由虚拟机自动建立的、低调度优先级的Finalizer线程去执行它们的finalize()方法。这里所说的“执行”是指虚拟机会触发这个方法开始运行，但并不承诺一定会等待它运行结束。这样做的原因是，如果某个对象的finalize()方法执行缓慢，或者更极端地发生了死循环，将很可能导致F-Queue队列中的其他对象永久处于等待，甚至导致整个内存回收子系统的崩溃。<br>finalize()方法是对象逃脱死亡命运的最后一次机会，稍后收集器将对F-Queue中的对象进行第二次小规模的标记，如果对象要在finalize()中成功拯救自己一只要重新与引用链上的任何-一个对象建立关联即可，譬如把自己(this关键字) 赋值给某个类变量或者对象的成员变量，那在第二次标记时它将被移出“即将回收”的集合，如果对象这时候还没有逃脱，那基本上它就真的要被回收了。</p>
<blockquote>
<p>尽管如此，但是一定要避免使用finalize()，这个方法不确定性太大了，无法保证其被调用的顺序。</p>
</blockquote>
<h3 id="回收方法区"><a href="#回收方法区" class="headerlink" title="回收方法区"></a>回收方法区</h3><p>《Java虚拟机规范》中提到过可以不要求虚拟机在方法区中实现垃圾收集，事实上也确实有未实现或未能完整实现方法区类型卸载的收集器存在(如JDK 11时期的ZGC收集器就不支持类卸载)， 方法区垃圾收集的“性价比”通常也是比较低的:在Java堆中，尤其是在新生代中，对常规应用进行一次垃圾收集通常可以回收70%至99%的内存空间，相比之下，方法区回收囿于苛刻的判定条件，其区域垃圾收集的回收成果往往远低于此。<br>方法区的垃圾回收两部分内容：</p>
<h4 id="废弃的常量和不再使用的类型"><a href="#废弃的常量和不再使用的类型" class="headerlink" title="废弃的常量和不再使用的类型"></a>废弃的常量和不再使用的类型</h4><p>回收废弃常量与回收Java堆中的对象非常类似。举个常量池中字面量回收的例子，假如一个字符串“java”曾经进入常量池中，但是当前系统又没有任何一个字符串对象的值是“java”,换句话说，已经没有任何字符串对象引用常量池中的“java”常量，且虚拟机中也没有其他地方引用这个字面量。如果在这时发生内存回收，而且垃圾收集器判断确有必要的话，这个“java”常量就将会被系统清理出常量池。<br>常量池中其他类(接口)、方法、字段的符号引用也与此类似。<br>判定一个类型是否属于“不再被使用的类”的条件就比较苛刻了。需要同时满足下面三个条件:</p>
<ul>
<li>该类所有的实例都已经被回收，也就是Java堆中不存在该类及其任何派生子类的实例。</li>
<li>该类对应的java.lang.Class对象没有在任何地方被引用，无法在任何地方通过反射访问该类的方法。</li>
<li>加载该类的类加载器已经被回收。</li>
</ul>
<p>关于对类型进行回收HotSpot虚拟机提供了一些参数做控制：</p>
<ul>
<li>-verbose: class</li>
<li>-XX: +TraceClassLoading</li>
<li>-XX: +TraceClassUnLoading（需要FastDebug版的虚拟机支持）</li>
</ul>
<h2 id="垃圾收集算法"><a href="#垃圾收集算法" class="headerlink" title="垃圾收集算法"></a>垃圾收集算法</h2><p>从如何判定对象消亡的角度出发，垃圾收集算法可以划分为：</p>
<ul>
<li>“引用计数式垃圾收集”(Reference Counting GC)（直接垃圾收集）</li>
<li>“追踪式垃圾收集”(Tracing GC)（间接垃圾收集）</li>
</ul>
<h3 id="分代收集理论"><a href="#分代收集理论" class="headerlink" title="分代收集理论"></a>分代收集理论</h3><ul>
<li>弱分代假说(Weak Generational Hypothesis)：绝大多数对象都是朝生夕灭的。</li>
<li>强分代假说(Strong Generational Hypothesis)：熬过越多次垃圾收集过程的对象就越难以消亡。</li>
</ul>
<p>收集器应该将Java堆划分出不同的区域，然后将回收对象依据其年龄(年龄即对象熬过垃圾收集过程的次数)分配到不同的区域之中存储。显而易见，如果一个区域中大多数对象都是朝生夕灭，难以熬过垃圾收集过程的话，那么把它们集中放在一起，每次回收时只关注如何保留少量存活而不是去标记那些大量将要被回收的对象，就能以较低代价回收到大量的空间;如果剩下的都是难以消亡的对象，那把它们集中放在一块，虚拟机便可以使用较低的频率来回收这个区域，这就同时兼顾了垃圾收集的时间开销和内存的空间有效利用。</p>
<p>在Java堆划分出不同的区域之后，垃圾收集器才可以每次只回收其中某一个或者某些部分的区域。<br>部分收集（Partial GC）类型划分：</p>
<ul>
<li>新生代收集（Minor GC/Young GC）：针对Java堆里面新生代的垃圾收集。</li>
<li>老年代收集（Major GC/Old GC）：针对Java堆里面老年代的垃圾收集，Major GC有些文献用来代表整堆收集，可能会混淆，目前只有CMS收集器会有单独收集老年代的行为。</li>
<li>混合收集（Mixed GC）：针对Java对里面的整个新生代和部分老年代的垃圾收集，目前只有G1收集器会有这种行为。</li>
</ul>
<p>整堆收集（Full GC）：针对整个Java堆和方法区的垃圾收集。</p>
<p>针对不同的区域安排与里面存储对象存亡特征相匹配的垃圾收集算法：</p>
<ul>
<li>标记-复制算法</li>
<li>标记-清除算法</li>
<li>标记-整理算法</li>
</ul>
<h4 id="新生代与老年代"><a href="#新生代与老年代" class="headerlink" title="新生代与老年代"></a>新生代与老年代</h4><p>现在的商用Java虚拟机里，设计者一般至少会把Java堆划分为新生代(Young Generation)和老年代(Old Generation)两个区域。<br>在新生代中，每次垃圾收集时都发现有大批对象死去，而每次回收后存活的少量对象，将会逐步晋升到老年代中存放。<br>但是存在一个明显的问题：<strong><em>对象不是孤立的，对象之间会存在跨代引用。</em></strong></p>
<p>假如要现在进行一次只局限于新生代区域内的收集(Minor GC)，但新生代中的对象是完全有可能被老年代所引用的，为了找出该区域中的存活对象，不得不在固定的GCRoots之外，再额外遍历整个老年代中所有对象来确保可达性分析结果的正确性，反过来也是一样以。遍历整个老年代所有对象的方案虽然理论上可行，但无疑会为内存回收带来很大的性能负担。因此可以对分代收集理论添加第三条经验法则:</p>
<ul>
<li>跨代引用假说(Intergenerational Reference Hypothesis)<br>跨代引用相对于同代引用来说仅占极少数。这其实是可根据前两条假说逻辑推理得出的隐含推论：存在互相引用关系的两个对象，是应该倾向于同时生存或者同时消亡的。<br>如果某个新生代对象存在跨代引用，由于老年代对象难以消亡，该引用会使得新生代对象在收集时同样得以存活，进而在年龄增长之后晋升到老年代中，这时跨代引用也随即被消除了。<br>依据这条假说，我们就不应再为了少量的跨代引用去扫描整个老年代，也不必浪费空间专门记录每一一个对象是否存在及存在哪些跨代引用，只需在新生代上建立一个全局的数据结构(该结构被称为“记忆集”，Remembered Set)， 这个结构把老年代划分成若干小块，标识出老年代的哪-块内存会存在跨代引用。此后当发生Minor GC时，只有包含了跨代引用的小块内存里的对象才会被加入到GCRoots进行扫描。虽然这种方法需要在对象改变引用关系(如将自己或者某个属性赋值)时维护记录数据的正确性，会增加一些运行时 的开销，但比起收集时扫描整个老年代来说仍然是划算的。</li>
</ul>
<h3 id="标记-清除算法（Mark-Sweep）"><a href="#标记-清除算法（Mark-Sweep）" class="headerlink" title="标记-清除算法（Mark-Sweep）"></a>标记-清除算法（Mark-Sweep）</h3><p>最基础的垃圾收集算法，算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象，在标记完成后，统一回收掉所有被标记的对象，也可以反过来，标记存活的对象，统一回收所有未被标记的对象。<br>标记-清除算法的缺点：</p>
<ul>
<li>第一个是执行效率不稳定，如果Java堆中包含大量对象，而且其中大部分是需要被回收的，这时必须进行大量标记和清除的动作，导致标记和清除两个过程的执行效率都随对象数量增长而降低;</li>
<li>第二个是内存空间的碎片化问题，标记、清除之后会产生大量不连续的内存碎片，空间碎片太多可能会导致当以后在程序运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。</li>
</ul>
<p><img src="https://gitee.com/fengorz/oss/raw/master/uPic/Wkxl6l.png" alt></p>
<h3 id="标记-复制算法"><a href="#标记-复制算法" class="headerlink" title="标记-复制算法"></a>标记-复制算法</h3><h4 id="半区复制"><a href="#半区复制" class="headerlink" title="半区复制"></a>半区复制</h4><p>为了解决标记清除算法面对大量可回收对象时执行效率低的问题，有人提出了一种称为“半区复制”(Semispace Copying)的垃圾收集算法，它将可用内存按容量划分为大小相等的两块，每次只使用其中的一块。当这一块的内存用完了，就将还存活着的对象复制到另外一块上面，然后再把已使用过的内存空间一次清理掉。如果内存中多数对象都是存活的，这种算法将会产生大量的内存间复制的开销，但对于多数对象都是可回收的情况，算法需要复制的就是占少数的存活对象，而且每次都是针对整个半区进行内存回收，分配内存时也就不用考虑有空间碎片的复杂情况，只要移动堆顶指针，按顺序分配即可。这样实现简单，运行高效，不过其缺陷也显而易见，这种复制回收算法的代价是将可用内存缩小为了原来的一半，另一半等同于空置不用。<br><img src="https://gitee.com/fengorz/oss/raw/master/uPic/mLYIu0.png" alt><br>新生代中的对象有98%熬不过第一轮收集。因此并不需要按照1 : 1的比例来划分新生代的内存空间，现在的商用Java虚拟机大多都优先采用了这种收集算法去回收新生代。</p>
<h4 id="Appel式回收"><a href="#Appel式回收" class="headerlink" title="Appel式回收"></a>Appel式回收</h4><p>之后有人继续提出了一种更优化的半区复制分代策略，现在称为“Appel式回收”。HotSpot虛拟机的Serial、ParNew等新生代收集器均采用了这种策略来设计新生代的内存布局”。<br>Appel式回收的具体做法是把新生代分为一块较大的<code>Eden空间</code>和两块较小的<code>Survivor空间</code>，每次分配内存只使用Eden和其中一块Survivor。发生垃圾搜集时，将Eden和Survivor中仍然存活的对象一次性复制到另外一块Survivor空间上，然后直接清理掉Eden和已用过的那块Survivor空间。HotSpot虚拟机默认Eden和Survivor的大小比例是8 : 1，也即<strong><em>每次新生代中可用内存空间为整个新生代容量的90%(Eden的80%加上一个Survivor的10%)，只有一个Survivor空间，即10%的新生代是会被“浪费”的。</em></strong><br>当然，98%的对象可被回收仅仅是“普通场景”下测得的数据，任何人都没有办法百分百保证每次回收都只有不多于10%的对象存活，因此Appel式回收还有一个充当罕<br>见情况的“逃生门”的安全设计，当Survivor空间不足以容纳一次Minor GC之后存活的对象时，就需要依赖其他内存区域(实际上大多就是老年代)进行<code>分配担保(Handle Promotion)</code>。</p>
<h3 id="标记-整理算法（Mark-Compact）"><a href="#标记-整理算法（Mark-Compact）" class="headerlink" title="标记-整理算法（Mark-Compact）"></a>标记-整理算法（Mark-Compact）</h3><p>标记复制算法在对象存活率较高时就要进行较多的复制操作，效率将会降低。更关键的是，如果不想浪费50%的空间，就需要有额外的空间进行分配担保，以应对被使用的内存中所有对象都100%存活的极端情况，所以在老年代一般不能直接选用这种算法。<br>针对老年代对象的存亡特征，1974年Edward Lueders提出了另外一种有针对性的“标记-整理”(Mark Compact)算法，其中的标记过程仍然与“标记-清除”算法一样，但后续步骤不是直接对可回收对象进行清理，而是让所有存活的对象都向内存空间一端移动，然后直接清理掉边界以外的内存。<br>标记-清除算法与标记-整理算法的本质差异在于：</p>
<ul>
<li>前者是一种非移动式的回收算法；</li>
<li>而后者是移动式的。</li>
</ul>
<p>移动回收后的存活对象是一项优缺点并存的风险决策。<br><img src="https://gitee.com/fengorz/oss/raw/master/uPic/xGECtR.png" alt></p>
<ul>
<li>如果移动存活的对象太多，这将是一笔很大的开销，而且这种移动操作要强制暂停用户的应用程序才能进行（最新的ZGC和Shenandoah收集器使用读屏障(Read Barrier)技术实现了整理过程与用户线程的并发执行）。</li>
<li>如果完全不考虑移动和整理存活对象的话，弥散于堆中的存活对象导致的空间碎片化问题就只能依赖更为复杂的内存分配器和内存访问器来解决。<br>譬如通过“分区空闲分配链表’’来解决内存分配问题(计算机硬盘存储大文件就不要求物理连续的磁盘空间，能够在碎片化的硬盘上存储和访问就是通过硬盘分区表实现的)。<br>内存的访问是用户程序最频繁的操作，在这个环节上增加了额外的负担，势必会直接影响应用程序的吞吐量。</li>
</ul>
<p>基于以上两点，是否移动对象都存在弊端，移动则内存回收时会更复杂，不移动则内存分配时会更复杂。从垃圾收集的停顿时间来看，不移动对象停顿时间会更短，甚至可以不需要停顿，但是从整个程序的吞吐量来看，移动对象会更划算。这里的吞吐量的实质是赋值器(Mutator, 可以理解为使用垃圾收集的用户程序)与收集器的效率总和。即使不移动对象会使得收集器的效率提升一些，但因内存分配和访问相比垃圾收集频率要高得多，这部分的耗时增加，总吞吐量仍然是下降的。<br>HotSpot虚拟机里面关注吞吐量的Parallel Scavenge收集器是基于标记整理算法的，而关注延迟的CMS收集器则是基于标记-清除算法的，这也从侧面印证这点。<br>另外，还有一种“和稀泥式”解决方案可以不在内存分配和访问上增加太大额外负担，做法是让虚拟机平时多数时间都采用标记清除算法，暂时容忍内存碎片的存在，直到内存空间的碎片化程度已经大到影响对象分配时，再采用标记-整理算法收集一次，以获得规整的内存空间。</p>
<blockquote>
<p>前面提到的基于标记-清除算法的CMS收集器面临空间碎片过多时采用的就是这种处理办法。</p>
</blockquote>
<h4 id="三色标记法（Tri-color-Marking）"><a href="#三色标记法（Tri-color-Marking）" class="headerlink" title="三色标记法（Tri-color Marking）"></a>三色标记法（Tri-color Marking）</h4><p>白色、黑色、灰色</p>
<h5 id="并发出现对象消失问题"><a href="#并发出现对象消失问题" class="headerlink" title="并发出现对象消失问题"></a>并发出现对象消失问题</h5><p>两种解决方案：增量更新（Incremental Update）、原始快照（Snapshot At The Begging，SATB）。</p>
<h2 id="经典垃圾收集器"><a href="#经典垃圾收集器" class="headerlink" title="经典垃圾收集器"></a>经典垃圾收集器</h2><p><img src="https://cdn.jsdelivr.net/gh/coding-by-feng/oss@master/uPic/WLBBDC.png" alt><br>如果两个收集器存在连线，则它们可以搭配使用。<br>上图连线关系不是一成不变的，在JDK 8时将Serial+CMS、ParNew+Serial Old这两个组合声明为废弃(JEP 173)，并在JDK 9中完全取消了这些组合的支持(JEP 214)。</p>
<h3 id="Serial收集器"><a href="#Serial收集器" class="headerlink" title="Serial收集器"></a>Serial收集器</h3><p>这是最古老，最基础的垃圾收集器，只会使用一个处理器单线程去做垃圾收集，而且它在进行垃圾收集期间，会停掉所有的用户工作线程，这是非常糟糕的用户体验。(Stop The World)<br>Serial/Serial Old收集器工作流程：<br><img src="https://cdn.jsdelivr.net/gh/coding-by-feng/oss@master/uPic/lqCCw4.png" alt><br>Serial虽然有上述这么大的弊端，但是历代JVM一直致力于Serial的优化改进，直至如今，Serial依然是HotSpot的默认新生代收集器，因为其在单线程的环境下相较而言简单高效，而且垃圾收集的时间甚至可以缩短到十几、几十毫秒。</p>
<h3 id="ParNew收集器"><a href="#ParNew收集器" class="headerlink" title="ParNew收集器"></a>ParNew收集器</h3><p>是Serial的多线程并行版本，具体实现和Serial收集器完全一致。<br>ParNew收集器工作流程：<br><img src="https://cdn.jsdelivr.net/gh/coding-by-feng/oss@master/uPic/gh4KtY.png" alt></p>
<p>JDK5发布时推出了划时代意义的CMS收集器，是HotSpot第一款支持并发的垃圾收集器，其首次实现垃圾收集器与用户线程（基本上）同时工作。<br>ParNew是HotSpot JVM中第一款退役的收集器。</p>
<h4 id="垃圾收集并行-vs-并发"><a href="#垃圾收集并行-vs-并发" class="headerlink" title="垃圾收集并行 vs 并发"></a>垃圾收集并行 vs 并发</h4><ul>
<li>并行(Parallel) ：并行描述的是多条垃圾收集器线程之间的关系，说明同一时间有多条这样的线程在协同工作，通常默认此时用户线程是处于等待状态。</li>
<li>并发(Concurrent) ：并发描述的是垃圾收集器线程与用户线程之间的关系，说明同一时间垃圾收集器线程与用户线程都在运行。由于用户线程并未被冻结，所以程序仍然能响应服务请求，但由于垃圾收集器线程占用了一部分系统资源，此时应用程序的处理的吞吐量将受到一定影响。</li>
</ul>
<h3 id="Parallel-Scavenge收集器（吞吐量优先收集器）"><a href="#Parallel-Scavenge收集器（吞吐量优先收集器）" class="headerlink" title="Parallel Scavenge收集器（吞吐量优先收集器）"></a>Parallel Scavenge收集器（吞吐量优先收集器）</h3><p>这是一款新生代收集器，同样是基于标记-复制算法实现的，也是能够并行收集的多线程收集器。<br>Parallel Scavenge的目标是要达到一个可控的吞吐量。</p>
<h4 id="吞吐量"><a href="#吞吐量" class="headerlink" title="吞吐量"></a>吞吐量</h4><p><img src="https://gitee.com/fengorz/oss/raw/master/uPic/9wrqFn.png" alt><br>高吞吐量可以更好地利用处理器资源，更快完成用户的任务。</p>
<h4 id="Parallel-Scavenge精确控制吞吐量的参数"><a href="#Parallel-Scavenge精确控制吞吐量的参数" class="headerlink" title="Parallel Scavenge精确控制吞吐量的参数"></a>Parallel Scavenge精确控制吞吐量的参数</h4><ul>
<li><code>-XX: MaxGCPauseMills</code>：最大垃圾收集停顿时间（并非越小越好，它与吞吐量是互斥的关系）。<br>这个参数允许的值是一个大于0的毫秒数，收集器将尽力保证内存回收花费的时间不超过用户设定值。不过不要异想天开地认为如果把这个参数的值设置得更小点就能使得系统的垃圾收集速度变得更快，垃圾收集停顿时间缩短是以牺牲吞吐量和新生代空间为代价换取的:系统把新生代调得小-些，收集300MB新生代肯定比收集500MB快，但这也直接导致垃圾收集发生得更频繁，原来10秒收集一次、每次停顿100毫秒，现在变成5秒收集一次、每次停顿70毫秒。停顿时间的确在下降，但吞吐量也降下来了。</li>
<li><code>-XX: GCTimeRatio</code>：直接设置吞吐量大小。<br>这个参数的值则应当是一个大于0小于100的整数，也就是垃圾收集时间占总时间的比率，相当于吞吐量的倒数。譬如把此参数设置为19，那允许的最大垃圾收集时间就占总时间的5%(即1/(1+19))，默认值为99，即允许最大1%(即1/(1+99))的垃圾收集时间。</li>
<li><code>-XX: +UseAdaptiveSizePolicy</code>：开关参数，开启之后虚拟机会根据当前系统运行情况收集性能监控信息，动态调整其它一些参数以提供最合适的停顿时间或者最大的吞吐量（<code>自适应的调节策略</code>）。</li>
</ul>
<h3 id="Serial-Old收集器"><a href="#Serial-Old收集器" class="headerlink" title="Serial Old收集器"></a>Serial Old收集器</h3><p>Serial Old是Serial收集器的老年代版本，它同样是一个单线程收集器，使用标记-整理算法。<br><img src="https://gitee.com/fengorz/oss/raw/master/uPic/cQDTyf.png" alt></p>
<h3 id="Parallel-Old收集器"><a href="#Parallel-Old收集器" class="headerlink" title="Parallel Old收集器"></a>Parallel Old收集器</h3><p>Parallel Old是Parallel Scavenge收集器的老年代版本，支持多线程并发收集，基于标记-整理算法实现。</p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-Hans">
    <link itemprop="mainEntityOfPage" href="http://yoursite.com/2020/09/09/English系列之定语从句/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="Fengorz">
      <meta itemprop="description" content="A Quiet Departure.">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Feng's Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2020/09/09/English系列之定语从句/" class="post-title-link" itemprop="url">English系列之定语从句</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">Posted on</span>

              <time title="Created: 2020-09-09 20:51:52" itemprop="dateCreated datePublished" datetime="2020-09-09T20:51:52+08:00">2020-09-09</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">Edited on</span>
                <time title="Modified: 2020-09-11 17:02:41" itemprop="dateModified" datetime="2020-09-11T17:02:41+08:00">2020-09-11</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">In</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/English/" itemprop="url" rel="index"><span itemprop="name">English</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h2 id="定语从句概述"><a href="#定语从句概述" class="headerlink" title="定语从句概述"></a>定语从句概述</h2><h3 id="形容词作定语与从句作定语比较"><a href="#形容词作定语与从句作定语比较" class="headerlink" title="形容词作定语与从句作定语比较"></a>形容词作定语与从句作定语比较</h3><p>当要表达复杂的意思时，往往需要借助于定语从句来完成，即要用定语从句来修饰某个名词或名词短语。比如当我们说“我不喜欢懒人”，这时只需要用一个形容词lazy来作定语就可以。<br>I don’t like lazy people.<br>如果要表达稍微复杂一点的意思，比如说“我不喜欢不守信用的人”，这时，简单地用形容词作定语就无能为力了，就要借助于一个句子来修饰名词，用作名词的定语，对名词进行限制。<br>I don’t like people who never keep their word.<br><img src="https://gitee.com/fengorz/oss/raw/master/uPic/KmgSIt.png" alt></p>
<h3 id="定语从句的两个重要概念：先行词和关系词"><a href="#定语从句的两个重要概念：先行词和关系词" class="headerlink" title="定语从句的两个重要概念：先行词和关系词"></a>定语从句的两个重要概念：先行词和关系词</h3><p>被定语从句所修饰的对象称为先行词。<br>重复指代先行词、起连接主句和从句的作用，并且在定语从句中充当一定成分的连接词称为关系词。<br>关系词有两个作用：</p>
<ul>
<li>代词的作用： 重复指代先行词并且在定语从句中充当一定成分。</li>
<li>连接作用：即连接主句和从句。</li>
</ul>
<h2 id="关系词在定语从句中充当的成分"><a href="#关系词在定语从句中充当的成分" class="headerlink" title="关系词在定语从句中充当的成分"></a>关系词在定语从句中充当的成分</h2><h3 id="关系词用作从句的主语"><a href="#关系词用作从句的主语" class="headerlink" title="关系词用作从句的主语"></a>关系词用作从句的主语</h3><p>比较简单句和定语从句，看看一个简单句如何变成定语从句。<br><strong><em>The woman</em></strong> is a famous dancer and she lives next door.<br>这个句中有一个“重复元素”，这里的she指代 the woman，即两句之间有一个交叉点，而通过这个交叉点建立起两句之间的联系。现在要用she lives next door来作定语修饰the woman，以告诉对方哪位女士是舞蹈演员。而在定语从句中，指代“人”的关系词要用who。<br>因此，上句可改写成：<br>The woman is a famous dancer who (=and she) lives next door.<br>这里的关系词who既取代了代词she也取代了连词and， 即who既具有代词的作用也起着连接的作用。另外，定语从句一般要紧跟在它所修饰的名词后面。因此，上一句又可进一步改写成：<br>The woman who lives next door is a famous dancer.<br>由此看到，关系词who既指代the woman，同时又在从句中作主语，而且还替换了and，起着连词的作用。<br>这位女士是一名著名的舞蹈演员，她就住在我家隔壁。</p>
<p><img src="https://gitee.com/fengorz/oss/raw/master/uPic/ONSgS8.png" alt><br>这里的关系词who取代and they，来引导定语从句who have a good sense of humor，修饰名词guys。<br>我喜欢有幽默感的男士。</p>
<p>在变换过程中药注意三点：</p>
<ul>
<li>用关系词who代替人称代词（如she或they）后，定语从句里就不能再保留这些代词了。比如不能说：<br>The woman who she lives next door is a famous dancer. <em><br>I like guys who they have a good sense of humor. </em></li>
<li>事实上，定语从句可以修饰一个主句当中的任何一个名词。但是，我们关注的往往是关系词在定语从句中所充当的成分，不关心定语从句是修饰主句的哪个名词。</li>
<li>定语从句的谓语动词形式是由关系词所指代的先行词来决定的，而不是取决于关系词本身。定语从句who lives next door中的who指代的woman是单数名词，故谓语用单数的lives；而定语从句who have a good sense of humor中的who指代的guys是复数名词，故谓语用复数的have。</li>
</ul>
<p>Anger is a thief who steals away the nice moments .<br>愤怒就如同一个窃贼，盗取了我们的幸福时光。<br>He who is not handsome at twenty, nor strong at thirty, nor rich at forty, nor wise at fifty , will never be handsome, strong, rich, or wise.<br>一个人若在20岁时不俊美、30岁时不健康、40岁时不富有、50岁时不明智，那么他将永远不会拥有这些。<br>There is this difference between happiness and wisdom: the person who thinks himself the happiest man really is so; but the person who thinks himself the wisest is generally the greatest fool.<br>幸福与智慧区别于此：认为自己是最幸福的，他真的就是最幸福的；而认为自己是最有智慧的，他却往往是最大的傻瓜。<br>The world is a book, and those who do not travel read only one page.<br>世界犹如一本书，而那些从不出门旅行的人仅仅读了这本书的一页。<br>Of course I am not talking about easy-come-easy-go friendship. I’m talking about friends who care deeply about each other, who support each other, who make life worth living.<br>当然了，我这里所说的友谊不是那种“来得快去得也快”的泛泛之交，而是那种彼此之间能真正互相关心的朋友，能够互相支持的朋友，能够让你的人生更有意义的朋友。</p>
<h3 id="关系词用作从句中动词的宾语"><a href="#关系词用作从句中动词的宾语" class="headerlink" title="关系词用作从句中动词的宾语"></a>关系词用作从句中动词的宾语</h3><p>关系词除了在定语从句中作主语外，还可以在从句中作宾语。而且，还可以进一步区分为从句动词的宾语和从句介词的宾语。<br><img src="https://gitee.com/fengorz/oss/raw/master/uPic/8WGhly.png" alt><br>在例句1）中，这里的him指代名词短语a friend，可以用关系词who来替代him，充当动词trust的宾语。又因为who有连词的作用，所以此时and就要去掉了，如例句2）所示。另一方面，关系词who要置于从句的开头，所以要把who移到I的前面，原句从而变成了下面的例句3）：<br>3）I’d really like to find a friend who I can trust completely.<br>我想找一个我完全能够信任的人做朋友。</p>
<p>He is the best grammar teacher who I have ever seen.<br>他是我见过的最好的语法老师。</p>
<p>It’s hard to have a friend who you can trust completely.<br>很难有一个你能完全信任的朋友。</p>
<h3 id="关系词用作从句中介词的宾语"><a href="#关系词用作从句中介词的宾语" class="headerlink" title="关系词用作从句中介词的宾语"></a>关系词用作从句中介词的宾语</h3><p><img src="https://gitee.com/fengorz/oss/raw/master/uPic/YGRKuS.png" alt><br>这里的who充当介词with的宾语。<br>我这里说的朋友，是指那些你几乎可以把一切与之分享的人。</p>
<p>I hope I never have a boss who I can’t talk to about my problems.<br>这里的who充当介词to的宾语。<br>我不希望有一个无法与其谈论我的问题的老板。</p>
<p>I’d prefer someone who I have something in common with — who I can talk to easily.<br>这里的关系词who分别充当介词with和to的宾语。<br>我喜欢那些与我有共同点的人，这样易交流。</p>
<h3 id="作宾语的关系词可以省去"><a href="#作宾语的关系词可以省去" class="headerlink" title="作宾语的关系词可以省去"></a>作宾语的关系词可以省去</h3><p>作宾语的关系词可以省去，不论关系词是作动词的宾语还是作介词的宾语都可以省去。比如以上讨论过的相关例句，可以分别改写成：<br>I’d really like to find a friend ( ) I can trust completely.<br>I hope I never have a boss ( ) I can’t talk to about my problems.</p>
<blockquote>
<p>在从句中作主语的关系词无法省去。比如不能说：</p>
</blockquote>
<p>I like guys ( ) have a good sense of humor. *</p>
<h2 id="关系代词与先行词的搭配关系"><a href="#关系代词与先行词的搭配关系" class="headerlink" title="关系代词与先行词的搭配关系"></a>关系代词与先行词的搭配关系</h2><p><img src="https://gitee.com/fengorz/oss/raw/master/uPic/gScElI.png" alt></p>
<h3 id="先行词指“人”，用关系代词who或whom"><a href="#先行词指“人”，用关系代词who或whom" class="headerlink" title="先行词指“人”，用关系代词who或whom"></a>先行词指“人”，用关系代词who或whom</h3><h3 id="who和whom的区别"><a href="#who和whom的区别" class="headerlink" title="who和whom的区别"></a>who和whom的区别</h3><ul>
<li>who的前面不能与介词搭配使用<br>因为who是主格形式，所以在介词的后面不能用who，比如不说with who<em>，而说with whom。<br>I’m talking about friends who you can share almost everything with.<br>不能说“I’m talking about friends with who you can share almost everything. </em>”。<br>而应该说“I’m talking about friends with whom you can share almost everything.”。<br>主格形式的who除了作定语从句的主语外，还可以充当定语从句的宾语或表语。</li>
<li>whom在定语从句中不能作主语，但可作宾语或表语<br>A rich person is not one who has the most, but is one who needs the least.<br>这里的who作从句的主语，所以不能用whom，不说whom has…*。<br>一个富有的人，不是因为他拥有的最多，而是因为他需要的最少。</li>
</ul>
<p>Only the people who have the patience to do simple things perfectly will acquire the skill to do difficult things easily.<br>这里的who作从句的主语，所以不能用whom，不说whom have…*。<br>只有有耐心圆满完成简单工作的人，才能够轻而易举地完成困难的事。</p>
<blockquote>
<p>总之，who可以作主语，也可以代替whom作宾语或表语，但不能用在介词后面。而因为whom是宾格形式，所以不能作主语。</p>
</blockquote>
<h3 id="表示人或物的所有关系，用whose"><a href="#表示人或物的所有关系，用whose" class="headerlink" title="表示人或物的所有关系，用whose"></a>表示人或物的所有关系，用whose</h3><p>whose表示所有关系，其作用就如同物主代词my，your，her，his，their和our，whose的后面要接名词。whose可指人也可指物。“whose+名词”在从句中可作主语、动词的宾语或介词的宾语。<br><img src="https://gitee.com/fengorz/oss/raw/master/uPic/7KbXpL.png" alt><br>例句2）里的whose代替了例句1）里的his，指friend，即表示人。<br>我认识一个朋友，他哥哥是一名流行歌手。</p>
<p><img src="https://gitee.com/fengorz/oss/raw/master/uPic/FzNNnM.png" alt><br>例句2）里whose代替了例句1）里的its，指schoolroom，即表示物。<br>这些孩子就坐在窗玻璃都打破了的教室里上课。</p>
<p>When I looked through the window, I saw a girl whose beauty took my breath away.<br>这里的whose beauty作从句的主语，whose指人即a girl。<br>当我朝窗外看去，猛然看见一个美女，其美貌让我惊讶不已。</p>
<p>It was a meeting whose importance I did not realize at the time.<br>这里的whose importance作从句动词realize的宾语，whose指物即a meeting。<br>这个会议的重要性当时我并没有意识到。</p>
<h3 id="先行词指“物”，用关系代词which"><a href="#先行词指“物”，用关系代词which" class="headerlink" title="先行词指“物”，用关系代词which"></a>先行词指“物”，用关系代词which</h3><p><img src="https://gitee.com/fengorz/oss/raw/master/uPic/z263sW.png" alt><br>这里的先行词是sentences，表示“物”，因此不能用who。要用which代替they，指代sentences，告诉对方“我们正在学习什么类型的句子”。<br>我们正在学习带有定语从句的句子。</p>
<p>关系词which所指代的先行词比较复杂，它除了可以指单个的名词外，还可以指代短语甚至是从句。</p>
<h4 id="先行词是单个的名词"><a href="#先行词是单个的名词" class="headerlink" title="先行词是单个的名词"></a>先行词是单个的名词</h4><p>Anger is a wind which blows out the lamp of the mind.<br>先行词是wind，即which指单个的名词。<br>愤怒是吹灭心灵之灯的风。</p>
<p>Perhaps it is human to appreciate little that which we have and to long for that which we have not.<br>先行词是that，即which指单个的名词。<br>或许，“不惜已获，贪求不得”是人类共同的弱点吧。</p>
<h4 id="先行词是一个短语"><a href="#先行词是一个短语" class="headerlink" title="先行词是一个短语"></a>先行词是一个短语</h4><p>which可以指代一个短语。<br>He likes climbing mountains, which is a good exercise.<br>先行词是主句中的一个动名词短语climbing mountains。<br>他喜欢爬山，这是一项很好的运动。</p>
<h4 id="先行词是一个句子"><a href="#先行词是一个句子" class="headerlink" title="先行词是一个句子"></a>先行词是一个句子</h4><p>which可以指代前面的整个句子。<br><img src="https://gitee.com/fengorz/oss/raw/master/uPic/sLf9DC.png" alt><br>汤姆迟到了，这让我很吃惊。</p>
<p>He tore up my photo, which upset me.<br>全句“he tore up my photo”作which的先行词。<br>他把我的照片给撕了，这让我很生气。</p>
<h3 id="that可以指代“人”，也可以指代“物”"><a href="#that可以指代“人”，也可以指代“物”" class="headerlink" title="that可以指代“人”，也可以指代“物”"></a>that可以指代“人”，也可以指代“物”</h3><p>关系代词that的意义和用法主要有以下三点：</p>
<ul>
<li>that既可指“人”也可指“物”，指“物”时可以替换which，指“人”时可以替换who；</li>
<li>that不用在非限制性定语从句中；</li>
<li>that不能用于介词的后面（同who一样）。</li>
</ul>
<p>Too many people spend money which they haven’t earned, to buy things which they don’t want, to impress people who they don’t like.<br>可以用that替换其中的which和who而将这句话改写成“Too many people spend money that they haven’t earned, to buy things that they don’t want, to impress people that they don’t like.”。<br>这里that的先行词有“物（money和things）”，也有“人（people）”。<br>作宾语的关系词可以省去，因此这句话也可说成“Too many people spend money ( ) they haven’t earned, to buy things ( ) they don’t want, to impress people ( ) they don’t like.”。<br>太多的人花他们还没挣到的钱，买他们不想要的东西，取悦他们不喜欢的人。</p>
<p>that与which和who在用法上的区别：</p>
<ul>
<li>先行词中同时出现“人”和“物”时，用that<br>The writer and his novels that the article deals with are quite familiar to us.<br>这里的先行词是the writer and his novels，即同时有“人（writer）”和“物（his novels）”，所以要选用that引导定语从句。<br>这篇文章里所说的这个作家以及他的小说，我们都比较熟悉。</li>
<li>先行词为指“物”的all，little，few，much，none和the first时, 用that<br>All that glitters is not gold.<br>这里的先行词是all，所以用that来引导定语从句。另外要注意的是，<strong><em>这里有一个半否定的问题：all…not要译成“并非都是”，不能译成全否定的“所有都不”。</em></strong><br>闪光的并非都是金子。<br>The book contains little that is useful.<br>这里的先行词是表示否定意义的代词little（几乎没有什么），所以用that来引导定语从句。<br>这本书里几乎没有有用的东西。<br>There is not much that can be done.<br>这里的先行词是代词much，所以用that来引导定语从句。<br>这没有什么可以做的了。<br>As long as you stand up to the difficulties, there are none that cannot be overcome.<br>这里的先行词是代词none，所以用that来引导定语从句。<br>只要你勇敢地去面对困难，就没有什么克服不了的。</li>
<li>先行词是不定代词something，anything，nothing和everything时，一般用that</li>
<li>先行词被any，only，all，every，no，some，much，few，little，序数词，形容词的最高级，the only，the one，the very，the right和the last等成分修饰时，用that<br>Tell us all things that you know.<br>把你知道的都告诉我们。<br>The very problem that I want to solve is like this.<br>我想解决的问题就像这个一样。<br>He is the only man that can speak four foreign languages in our company.<br>他是我们公司唯一一个会说四种外语的人。</li>
<li>that不用在非限制性定语从句中<br>I became a single mother overnight, which is nothing like becoming famous overnight.<br>我在一夜之间成了单亲妈妈，这跟一夜成名可完全不同。<br>不能说“I became a single mother overnight, that is nothing like becoming famous overnight. *”。</li>
<li>that不能用于介词后面<br>The world in which we live is made up of matter. 我们所生活的世界是由物质组成的。<br>或：The world which we live in is made up of matter.<br>或：The world that we live in is made up of matter.<br>但不能说“The world in that we live is made up of matter. *”。</li>
</ul>
<h2 id="关系副词的用法"><a href="#关系副词的用法" class="headerlink" title="关系副词的用法"></a>关系副词的用法</h2><h3 id="when的用法"><a href="#when的用法" class="headerlink" title="when的用法"></a>when的用法</h3><h4 id="先行词指时间"><a href="#先行词指时间" class="headerlink" title="先行词指时间"></a>先行词指时间</h4><p>when可以引导限制性和非限制性定语从句，其先行词必须是表示时间的名词，如day，year或time等。<br>I’ll never forget the day when I met you.<br>这里的先行词是表示时间的名词day，所以用when引导定语从句。when作从句的时间状语，修饰谓语动词met。<br>我永远不会忘记我们见面的那一天。</p>
<p>My favorite season is spring, when the earth seems born again.<br>这里是when引导的非限制性定语从句，先行词是表示时间的名词spring。<br>到了春天，万物复苏，所以春天是我最喜欢的季节。</p>
<p>We will put off the outing until next week, when we won’t be so busy.<br>这里是when引导的非限制性定语从句，先行词是表示时间的短语next week。<br>我们要把出游推迟到下周，到时我们就不会这么忙了。</p>
<p>The day may soon come when we don’t bother to go to office but just work at home.<br>也许在不久的将来，我们就不必去办公室上班了，而就在家里工作。</p>
<h4 id="关系副词when在从句中作时间状语"><a href="#关系副词when在从句中作时间状语" class="headerlink" title="关系副词when在从句中作时间状语"></a>关系副词when在从句中作时间状语</h4><p>when是在定语从句中作时间状语。换句话说，若定语从句不是缺少状语，而是缺少主语或宾语，那么即使先行词是表示时间的名词，也不能用when而要用which或that来引导定语从句。因为用when来引导定语从句的前提条件是：when必须在其引导的定语从句中作时间状语。<br>1） I’ll never forget the time which I spent on campus.<br>2） I’ll never forget the day when we first met in the park.<br>在例句1）中，由于定语从句中的谓语spent缺宾语，因此关系词要充当这一宾语，故要用which。<br>在例句2）中，定语从句不缺主语或宾语，而是缺少时间状语，故关系词用when，这里when作时间状语，修饰met。<br>1）我永远不会忘记在大学校园里度过的时光。<br>2）我永远不会忘记我们第一次在公园里相遇的那一天。</p>
<p>The day, which began brightly, ended with a violent storm.<br>这里的先行词是day，表示时间。由于定语从句缺少主语，所以用关系代词which引导定语从句，which作从句的主语，而不用关系副词when，因为关系副词无法作主语，只能作状语。<br>那天，开始的时候还是阳光明媚，后来就下起了暴风雨。</p>
<h3 id="where的用法"><a href="#where的用法" class="headerlink" title="where的用法"></a>where的用法</h3><h4 id="where的先行词指地点"><a href="#where的先行词指地点" class="headerlink" title="where的先行词指地点"></a>where的先行词指地点</h4><p>where引导定语从句，其先行词一般是表示地点的名词，如place和house等。<br>A: Since you work in the theater, can’t you get me a free ticket now and then?<br>B: Certainly if you bring me a few notes now and then from the bank where you work!<br>这里的先行词bank表示地点，where在从句中充当地点状语（定语从句中不缺主语或宾语），修饰work。<br>A：既然你在剧院工作，那你就不能时常给我弄些免费的票吗？<br>B：当然可以，只要你能够从你工作的那家银行里时常给我弄些票子出来。</p>
<p>This is the town where I spent my childhood.<br>这就是我度过童年的小镇。</p>
<h4 id="关系副词where在从句中作地点状语"><a href="#关系副词where在从句中作地点状语" class="headerlink" title="关系副词where在从句中作地点状语"></a>关系副词where在从句中作地点状语</h4><p>与when类似，并不是先行词是表示地点的名词时，都得由where来引导定语从句，这得看关系词在从句中充当的成分。若定语从句缺主语或宾语，则要用which或that引导定语从句。<br>1） This is the town where I spent my childhood.<br>2） This is the town which I told you about before.<br>在例句1）中，定语从句不缺少主语或宾语，而是缺少地点状语，故关系词用where。where作地点状语，修饰spent。<br>在例句2）里，由于定语从句中的谓语部分told you about缺少宾语，因此关系词要充当这一宾语，故要用which。<br>1）这就是我度过童年的小城。<br>2）这就是我以前告诉过你的小城。</p>
<p>I’ve never been to Beijing, but it’s the place <strong>__</strong>.<br>A. where I’d like to visit<br>B. in which I’d like to visit<br>C. I most want to visit<br>D. that I want to visit it most<br>本题考查定语从句关系词的选择。这句话中尽管先行词是表示地点的名词place，但由于从句中的谓语visit缺少宾语，所以不能选A，而C才是正确答案。这里相当于省去了关系词that或which，因此原句应该是that / which I most want to visit。<br>我从未到过北京，但北京却是我最想游览的地方。</p>
<h3 id="why的用法"><a href="#why的用法" class="headerlink" title="why的用法"></a>why的用法</h3><p>why用来表示原因，只引导限制性定语从句，先行词是reason等表示原因的名词。<br>This is the reason why I didn’t come here.<br>此处的why相当于for which。<br>这就是我没来的原因。</p>
<h2 id="限制性与非限制性定语从句"><a href="#限制性与非限制性定语从句" class="headerlink" title="限制性与非限制性定语从句"></a>限制性与非限制性定语从句</h2><p>在限制性定语从句中，先行词和关系词之间不用逗号隔开，而非限制性定语从句则需要逗号隔开。</p>
<h3 id="限制性定语从句——不用逗号隔开"><a href="#限制性定语从句——不用逗号隔开" class="headerlink" title="限制性定语从句——不用逗号隔开"></a>限制性定语从句——不用逗号隔开</h3><p>限制性定语从句是用来对一个名词提供必要的信息（a defining relative clause defines or gives essential information about a noun），以说明定语从句所修饰的先行词的身份或性质，或用来限制先行词所指的范围。若去掉限制性定语从句，则主句部分的含义不明确，或意义不完整，所以不能用逗号来分隔先行词和定语从句。<br>比如“I don’t like people.”，这句话的意思不明确，句意不完整，因此其后面必须加一个限制性定语从句，才能使句意完整。比如：<br>I don’t like people who are never on time. 我不喜欢不守时的人。<br>I don’t like people who never keep their word. 我不喜欢不守信用的人。<br>以上句子黑体部分的定语从句都用来说明people的性质，是必不可少的信息，因此不能加逗号将其变为非限制性定语从句。</p>
<p>There are two factors which determine an individual’s intelligence.<br>这里若只是说“There are two factors.”，则句意不够完整，现在有了限制性定语从句which determine an individual’s intelligence来限制、修饰名词factors，就使得句意更明确。<br>决定人的智力的因素有两个。</p>
<p>He is a man who is of value to the people.<br>这里若只是说“He is a man.”，则句意不完整，现在有了限制性定语从句who is of value to the people来限制修饰名词man，就使得句意完整而明确。<br>他是一个有利于人民的人。</p>
<h3 id="非限制性定语从句——用逗号隔开"><a href="#非限制性定语从句——用逗号隔开" class="headerlink" title="非限制性定语从句——用逗号隔开"></a>非限制性定语从句——用逗号隔开</h3><p>非限制性定语从句用来提供附加的而非必要的信息，只是对先行词做进一步的解释、补充或说明（a non-defining relative clause gives optional information about a noun）。若去掉此定语从句，整个主句的意义一样很明确，不会引起误解和歧义。</p>
<h4 id="专有名词"><a href="#专有名词" class="headerlink" title="专有名词"></a>专有名词</h4><p>如果定语从句修饰一个专有名词，一般要用逗号分隔先行词与定语从句，也就是说要使用非限制性定语从句。这是因为专有名词本身的意思已经很完整，不需要限制，用定语从句只是对它进行补充说明。<br>My mother, who is 50 this year , lives with me now.<br>这里的先行词my mother就是一个专有名词，其后的定语从句who is 50 this year只能是作为非限制性的定语从句来提供附加信息，不能使用限制性定语从句。若说“My mother who is 50 this year lives with me now.”，则言外之意为“我有好几个母亲，目前和我住在一起的是今年50岁的那位母亲”。<br>我母亲今年50岁了，现在和我生活在一起。</p>
<p>I asked my mother, who is 80 , whether she’d ever seen snow in Iraq before, and her answer was no.<br>我问我已经80岁的妈妈，她以前在伊拉克是否见过雪，她回答说没见过。</p>
<p>Einstein, who was a great Jewish scientist, created the theory of relativity.<br>爱因斯坦这位伟大的犹太科学家创立了相对论。</p>
<p>Hawaii, which consists of eight principal islands, is a favorite vacation spot.<br>夏威夷包含八个主要岛屿，是一个度假胜地。</p>
<h4 id="类指名词"><a href="#类指名词" class="headerlink" title="类指名词"></a>类指名词</h4><p>普通名词若是用来表示类指，即表示的是一类事物，而非具体的某一个事物，此时，其后也宜用非限制性定语从句。<br>1） An elephant, which is the earth’s largest land mammal , has few natural enemies other than human beings.<br>2） One of the elephants which we saw at the zoo had only one tusk.<br>例句1）中的an elephant是类指或泛指，即表示“大象”这类动物，而不是指称具体的某一头大象。<br>例句2）中的one of the elephants则不是类指概念，而是具体指代某一头大象，即“我们在动物园看到的那头大象”。<br>1）大象作为陆地上体形最大的哺乳类动物，很少有天敌，除了人类之外。<br>2）我们在动物园看到有一头大象只有一颗象牙。</p>
<h3 id="用或不用逗号，句意有差别"><a href="#用或不用逗号，句意有差别" class="headerlink" title="用或不用逗号，句意有差别"></a>用或不用逗号，句意有差别</h3><p>还有一类句子，用限制性或非限制性定语从句都可以，但强调的意义不一样，即句意不同。<br>1） He has a daughter who works in a hospital.<br>2） He has a daughter, who works in a hospital.</p>
<p>1） The cab drivers who knew about the traffic jam took another road.<br>2） The cab drivers, who knew about the traffic jam, took another road.<br>例句1）的言外之意是“不知道的司机则没有换路线，只有部分司机换行驶路线了”。<br>例句2）的言外之意是“全部司机都知道，都换了行驶路线”。<br>1）知道那里堵车的出租车司机换了另一条路。<br>2）出租车司机知道那里堵车了，他们都换了另一条路。</p>
<h3 id="非限制性定语从句的翻译"><a href="#非限制性定语从句的翻译" class="headerlink" title="非限制性定语从句的翻译"></a>非限制性定语从句的翻译</h3><p>限制性定语从句一般翻译成定语的形式“……的”，而非限制性定语从句往往会译成各种状语形式。<br>The food, which wasn’t in the fridge , all went off.<br>食物都坏了，因为没有放在冰箱里。（译成原因状语从句）</p>
<p>The Ambassador gave a dinner to the scientists, with whom he especially wished to talk.<br>大使宴请了那些科学家，因为他特别想与他们交谈一下。（译成原因状语从句）</p>
<p>The people were desperate for work, any work, which could support their family.<br>人们急于找到工作，什么工作都行，只要能养家糊口。（译成条件状语从句）</p>
<p>The millionaire had another house built, which he didn’t need at all.<br>那位百万富翁又建了一幢房子，尽管他并不需要。（译成让步状语从句）</p>
<p>They tried to stamp out the revolt, which spread all the more furiously throughout the country.<br>起义依然声势浩大地遍布全国，尽管他们试图镇压。（译成让步状语从句）</p>
<h2 id="定语从句与同位语从句的区分"><a href="#定语从句与同位语从句的区分" class="headerlink" title="定语从句与同位语从句的区分"></a>定语从句与同位语从句的区分</h2><p>二者在结构上有相似性，都是“名词that从句”，这里的名词都称作先行词。</p>
<h3 id="从结构上区分：that是否作成分"><a href="#从结构上区分：that是否作成分" class="headerlink" title="从结构上区分：that是否作成分"></a>从结构上区分：that是否作成分</h3><p>that在定语从句中要充当成分，比如作从句的主语或宾语；而that在同位语从句中不充当任何成分，仅起连词的作用。<br>换句话说，定语从句的结构是“名词+that+不完整的句子”，而同位语从句的结构是“名词that+完整的陈述句”。<br>1） The rumor that he spread everywhere turned out to be untrue.<br>2） The rumor that Tom was a thief turned out to be untrue.<br>例句 1）中的that在从句中充当spread的宾语，故此处为定语从句。<br>例句2）中 的that在从句中不充当任何成分，因为从句“Tom was a thief”是一个完整的句子，不缺少任何成分。故其为同位语从句，作the rumor的同位语，补充说明the rumor的具体内容。<br>1）他到处散布的那个谣言，结果证明是假的。<br>2）有谣传说汤姆是小偷，结果证明是假的。</p>
<p>1） The fact that we talked about is very important.<br>2） The fact that he succeeded in the experiment pleased everybody.<br>例句1）中的that作talked about的宾语，故此处为定语从句。<br>例句2）中的that不充当成分，因为“he succeeded in the experiment”是一个完整的陈述句，故其为同位语从句。<br>1）我们讨论的这些情况很重要。<br>2）他的实验取得了成功，这让大家很高兴。</p>
<h3 id="从意义上区分：that引导的从句的意义不同"><a href="#从意义上区分：that引导的从句的意义不同" class="headerlink" title="从意义上区分：that引导的从句的意义不同"></a>从意义上区分：that引导的从句的意义不同</h3><p>定语从句相当于形容词, 对先行词起修饰、描述和限制的作用；同位语从句相当于一个名词，是对其前面名词内容的具体表述，二者是同位关系。<br>1） The news that you heard is not true.<br>2） The news that Lincoln was murdered at a theater is true.<br>例句1）中的定语从句that you heard并不是表示news的内容，只是在限制news所指的范围，即“你听到的那个消息”。<br>例句2）中的同位语从句that Lincoln was murdered at a theater是表示news的具体 内容，在意义上与news构成同位关系。<br>1）你听到的那个消息并不是真实的。<br>2）有消息说林肯是在一个剧院被刺杀的，这是真的。</p>
<p>1） The fact that you pointed out made me interested.<br>2） The fact that there are no lives on the moon is known to us all.<br>例句1）中的定语从句that you pointed out并不是表示fact的内容，只是在限制fact的所指范围，即“你指出的这个事实”。<br>例句2）中的同位语从句that there are no lives on the moon是表示fact的具体内容，在意义上与fact构成同位关系。<br>1）你指出的这一点，我很感兴趣。<br>2）众所周知，月球上并没有生命存在。</p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-Hans">
    <link itemprop="mainEntityOfPage" href="http://yoursite.com/2020/07/22/English系列-自然拼读/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="Fengorz">
      <meta itemprop="description" content="A Quiet Departure.">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Feng's Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2020/07/22/English系列-自然拼读/" class="post-title-link" itemprop="url">English系列-自然拼读</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">Posted on</span>

              <time title="Created: 2020-07-22 16:51:35" itemprop="dateCreated datePublished" datetime="2020-07-22T16:51:35+08:00">2020-07-22</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">Edited on</span>
                <time title="Modified: 2020-09-10 10:02:46" itemprop="dateModified" datetime="2020-09-10T10:02:46+08:00">2020-09-10</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">In</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/English/" itemprop="url" rel="index"><span itemprop="name">English</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h2 id="单词结尾的s什么时候读-“s”-与-“z”"><a href="#单词结尾的s什么时候读-“s”-与-“z”" class="headerlink" title="单词结尾的s什么时候读 “s” 与 “z”"></a>单词结尾的s什么时候读 “s” 与 “z”</h2><ul>
<li>做动词时结尾读”z”</li>
<li>做形容词、副词、名词时结尾读”s”</li>
</ul>
<p><img src="https://gitee.com/fengorz/oss/raw/master/uPic/aXaxF4.png" alt><br><img src="https://gitee.com/fengorz/oss/raw/master/uPic/kwz7k7.png" alt><br><img src="https://gitee.com/fengorz/oss/raw/master/uPic/G9b1HK.png" alt><br><img src="https://gitee.com/fengorz/oss/raw/master/uPic/s6UeVk.png" alt><br><img src="https://gitee.com/fengorz/oss/raw/master/uPic/qJ0Dzb.png" alt><br><img src="https://gitee.com/fengorz/oss/raw/master/uPic/odIBhw.png" alt><br><img src="https://gitee.com/fengorz/oss/raw/master/uPic/q8mAhm.png" alt></p>
<blockquote>
<p>参考来源<a href="https://www.zhihu.com/zvideo/1268214408456630272" target="_blank" rel="noopener">知乎</a></p>
</blockquote>
<h1 id="字母组合发音"><a href="#字母组合发音" class="headerlink" title="字母组合发音"></a>字母组合发音</h1><h2 id="ar"><a href="#ar" class="headerlink" title="ar"></a>ar</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/ɑː/</span><br></pre></td></tr></table></figure>
<p>garbage</p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  


  
  <nav class="pagination">
    <span class="page-number current">1</span><a class="page-number" href="/page/2/">2</a><span class="space">&hellip;</span><a class="page-number" href="/page/25/">25</a><a class="extend next" rel="next" href="/page/2/"><i class="fa fa-angle-right" aria-label="Next page"></i></a>
  </nav>



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          Table of Contents
        </li>
        <li class="sidebar-nav-overview">
          Overview
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">Fengorz</p>
  <div class="site-description" itemprop="description">A Quiet Departure.</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">125</span>
          <span class="site-state-item-name">posts</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">68</span>
        <span class="site-state-item-name">categories</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">87</span>
        <span class="site-state-item-name">tags</span></a>
      </div>
  </nav>
</div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Fengorz</span>
</div>
  <div class="powered-by">Powered by <a href="https://hexo.io" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.org" class="theme-link" rel="noopener" target="_blank">NexT.Gemini</a>
  </div>

        








      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js"></script>
  <script src="/lib/velocity/velocity.min.js"></script>
  <script src="/lib/velocity/velocity.ui.min.js"></script>
<script src="/js/utils.js"></script><script src="/js/motion.js"></script>
<script src="/js/schemes/pisces.js"></script>
<script src="/js/next-boot.js"></script>



  















  

  

</body>
</html>
